Merge develop
This commit is contained in:
commit
88f92693f2
|
@ -39,6 +39,7 @@ Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
|||
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production
|
||||
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section)
|
||||
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section)
|
||||
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
|
||||
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
|
||||
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
||||
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
|
||||
|
|
|
@ -27,6 +27,12 @@
|
|||
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
|
||||
config :web_push_encryption, :vapid_details,
|
||||
subject: "mailto:administrator@example.com",
|
||||
public_key:
|
||||
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||
|
||||
try do
|
||||
import_config "test.secret.exs"
|
||||
rescue
|
||||
|
|
|
@ -29,6 +29,12 @@ config :pleroma, Pleroma.Repo,
|
|||
hostname: "<%= dbhost %>",
|
||||
pool_size: 10
|
||||
|
||||
# Configure web push notifications
|
||||
config :web_push_encryption, :vapid_details,
|
||||
subject: "mailto:<%= email %>",
|
||||
public_key: "<%= web_push_public_key %>",
|
||||
private_key: "<%= web_push_private_key %>"
|
||||
|
||||
# Enable Strict-Transport-Security once SSL is working:
|
||||
# config :pleroma, :http_security,
|
||||
# sts: true
|
||||
|
@ -54,9 +60,9 @@ config :pleroma, Pleroma.Repo,
|
|||
|
||||
|
||||
# Configure Openstack Swift support if desired.
|
||||
#
|
||||
# Many openstack deployments are different, so config is left very open with
|
||||
# no assumptions made on which provider you're using. This should allow very
|
||||
#
|
||||
# Many openstack deployments are different, so config is left very open with
|
||||
# no assumptions made on which provider you're using. This should allow very
|
||||
# wide support without needing separate handlers for OVH, Rackspace, etc.
|
||||
#
|
||||
# config :pleroma, Pleroma.Uploaders.Swift,
|
||||
|
|
|
@ -66,7 +66,8 @@ def start(_type, _args) do
|
|||
),
|
||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||
worker(Pleroma.Web.Federator, []),
|
||||
worker(Pleroma.Stats, [])
|
||||
worker(Pleroma.Stats, []),
|
||||
worker(Pleroma.Web.Push, [])
|
||||
] ++
|
||||
streamer_child() ++
|
||||
chat_child() ++
|
||||
|
|
|
@ -114,7 +114,7 @@ def add_user_links({subs, text}, mentions) do
|
|||
|
||||
subs =
|
||||
subs ++
|
||||
Enum.map(mentions, fn {match, %User{ap_id: ap_id, info: info}, uuid} ->
|
||||
Enum.map(mentions, fn {match, %User{id: id, ap_id: ap_id, info: info}, uuid} ->
|
||||
ap_id =
|
||||
if is_binary(info.source_data["url"]) do
|
||||
info.source_data["url"]
|
||||
|
@ -125,7 +125,7 @@ def add_user_links({subs, text}, mentions) do
|
|||
short_match = String.split(match, "@") |> tl() |> hd()
|
||||
|
||||
{uuid,
|
||||
"<span><a class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
|
||||
"<span><a data-user='#{id}' class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
|
||||
end)
|
||||
|
||||
{subs, uuid_text}
|
||||
|
@ -147,7 +147,11 @@ def add_hashtag_links({subs, text}, tags) do
|
|||
subs =
|
||||
subs ++
|
||||
Enum.map(tags, fn {tag_text, tag, uuid} ->
|
||||
url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag_text}</a>"
|
||||
url =
|
||||
"<a data-tag='#{tag}' href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{
|
||||
tag_text
|
||||
}</a>"
|
||||
|
||||
{uuid, url}
|
||||
end)
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
|||
Meta.strip_comments()
|
||||
|
||||
# links
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||
|
||||
# paragraphs and linebreaks
|
||||
|
@ -86,7 +86,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
|||
Meta.remove_cdata_sections_before_scrub()
|
||||
Meta.strip_comments()
|
||||
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
||||
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
||||
|
||||
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
||||
|
|
|
@ -3,7 +3,12 @@ defmodule Pleroma.HTTP.Connection do
|
|||
Connection for http-requests.
|
||||
"""
|
||||
|
||||
@hackney_options [pool: :default]
|
||||
@hackney_options [
|
||||
pool: :default,
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
follow_redirect: true
|
||||
]
|
||||
@adapter Application.get_env(:tesla, :adapter)
|
||||
|
||||
@doc """
|
||||
|
|
|
@ -110,6 +110,7 @@ def create_notification(%Activity{} = activity, %User{} = user) do
|
|||
notification = %Notification{user_id: user.id, activity: activity}
|
||||
{:ok, notification} = Repo.insert(notification)
|
||||
Pleroma.Web.Streamer.stream("user", notification)
|
||||
Pleroma.Web.Push.send(notification)
|
||||
notification
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
defmodule Pleroma.Object do
|
||||
use Ecto.Schema
|
||||
alias Pleroma.{Repo, Object, Activity}
|
||||
alias Pleroma.{Repo, Object, User, Activity}
|
||||
import Ecto.{Query, Changeset}
|
||||
|
||||
schema "objects" do
|
||||
|
@ -31,6 +31,13 @@ def normalize(obj) when is_map(obj), do: Object.get_by_ap_id(obj["id"])
|
|||
def normalize(ap_id) when is_binary(ap_id), do: Object.get_by_ap_id(ap_id)
|
||||
def normalize(_), do: nil
|
||||
|
||||
# Owned objects can only be mutated by their owner
|
||||
def authorize_mutation(%Object{data: %{"actor" => actor}}, %User{ap_id: ap_id}),
|
||||
do: actor == ap_id
|
||||
|
||||
# Legacy objects can be mutated by anybody
|
||||
def authorize_mutation(%Object{}, %User{}), do: true
|
||||
|
||||
if Mix.env() == :test do
|
||||
def get_cached_by_ap_id(ap_id) do
|
||||
get_by_ap_id(ap_id)
|
||||
|
|
|
@ -1,30 +1,70 @@
|
|||
defmodule Pleroma.Plugs.OAuthPlug do
|
||||
import Plug.Conn
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
import Ecto.Query
|
||||
|
||||
def init(options) do
|
||||
options
|
||||
end
|
||||
alias Pleroma.{
|
||||
User,
|
||||
Repo,
|
||||
Web.OAuth.Token
|
||||
}
|
||||
|
||||
@realm_reg Regex.compile!("Bearer\:?\s+(.*)$", "i")
|
||||
|
||||
def init(options), do: options
|
||||
|
||||
def call(%{assigns: %{user: %User{}}} = conn, _), do: conn
|
||||
|
||||
def call(conn, _) do
|
||||
token =
|
||||
case get_req_header(conn, "authorization") do
|
||||
["Bearer " <> header] -> header
|
||||
_ -> get_session(conn, :oauth_token)
|
||||
end
|
||||
|
||||
with token when not is_nil(token) <- token,
|
||||
%Token{user_id: user_id} <- Repo.get_by(Token, token: token),
|
||||
%User{} = user <- Repo.get(User, user_id),
|
||||
false <- !!user.info.deactivated do
|
||||
with {:ok, token} <- fetch_token(conn),
|
||||
{:ok, user} <- fetch_user(token) do
|
||||
conn
|
||||
|> assign(:token, token)
|
||||
|> assign(:user, user)
|
||||
else
|
||||
_ -> conn
|
||||
end
|
||||
end
|
||||
|
||||
# Gets user by token
|
||||
#
|
||||
@spec fetch_user(String.t()) :: {:ok, User.t()} | nil
|
||||
defp fetch_user(token) do
|
||||
query = from(q in Token, where: q.token == ^token, preload: [:user])
|
||||
|
||||
with %Token{user: %{info: %{deactivated: false} = _} = user} <- Repo.one(query) do
|
||||
{:ok, user}
|
||||
end
|
||||
end
|
||||
|
||||
# Gets token from session by :oauth_token key
|
||||
#
|
||||
@spec fetch_token_from_session(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token_from_session(conn) do
|
||||
case get_session(conn, :oauth_token) do
|
||||
nil -> :no_token_found
|
||||
token -> {:ok, token}
|
||||
end
|
||||
end
|
||||
|
||||
# Gets token from headers
|
||||
#
|
||||
@spec fetch_token(Plug.Conn.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token(%Plug.Conn{} = conn) do
|
||||
headers = get_req_header(conn, "authorization")
|
||||
|
||||
with :no_token_found <- fetch_token(headers),
|
||||
do: fetch_token_from_session(conn)
|
||||
end
|
||||
|
||||
@spec fetch_token(Keyword.t()) :: :no_token_found | {:ok, String.t()}
|
||||
defp fetch_token([]), do: :no_token_found
|
||||
|
||||
defp fetch_token([token | tail]) do
|
||||
trimmed_token = String.trim(token)
|
||||
|
||||
case Regex.run(@realm_reg, trimmed_token) do
|
||||
[_, match] -> {:ok, String.trim(match)}
|
||||
_ -> fetch_token(tail)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -56,7 +56,7 @@ defmodule Pleroma.ReverseProxy do
|
|||
@hackney Application.get_env(:pleroma, :hackney, :hackney)
|
||||
@httpoison Application.get_env(:pleroma, :httpoison, HTTPoison)
|
||||
|
||||
@default_hackney_options [{:follow_redirect, true}]
|
||||
@default_hackney_options []
|
||||
|
||||
@inline_content_types [
|
||||
"image/gif",
|
||||
|
|
|
@ -62,10 +62,6 @@ def follow_changeset(struct, params \\ %{}) do
|
|||
|> validate_required([:following])
|
||||
end
|
||||
|
||||
def info_changeset(struct, params \\ %{}) do
|
||||
raise "NOT VALID ANYMORE"
|
||||
end
|
||||
|
||||
def user_info(%User{} = user) do
|
||||
oneself = if user.local, do: 1, else: 0
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ defmodule Pleroma.User.Info do
|
|||
field(:topic, :string, default: nil)
|
||||
field(:hub, :string, default: nil)
|
||||
field(:salmon, :string, default: nil)
|
||||
field(:hide_network, :boolean, default: false)
|
||||
|
||||
# Found in the wild
|
||||
# ap_id -> Where is this used?
|
||||
|
@ -135,6 +136,7 @@ def profile_update(info, params) do
|
|||
:no_rich_text,
|
||||
:default_scope,
|
||||
:banner,
|
||||
:hide_network,
|
||||
:background
|
||||
])
|
||||
end
|
||||
|
@ -147,6 +149,11 @@ def mastodon_profile_update(info, params) do
|
|||
])
|
||||
end
|
||||
|
||||
def mastodon_settings_update(info, params) do
|
||||
info
|
||||
|> cast(params, [:settings])
|
||||
end
|
||||
|
||||
def set_source_data(info, source_data) do
|
||||
params = %{source_data: source_data}
|
||||
|
||||
|
|
|
@ -574,7 +574,14 @@ def fetch_activities_bounded(recipients_to, recipients_cc, opts \\ %{}) do
|
|||
|
||||
def upload(file, opts \\ []) do
|
||||
with {:ok, data} <- Upload.store(file, opts) do
|
||||
Repo.insert(%Object{data: data})
|
||||
obj_data =
|
||||
if opts[:actor] do
|
||||
Map.put(data, "actor", opts[:actor])
|
||||
else
|
||||
data
|
||||
end
|
||||
|
||||
Repo.insert(%Object{data: obj_data})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -765,10 +772,7 @@ def fetch_and_contain_remote_object_from_id(id) do
|
|||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
@httpoison.get(
|
||||
id,
|
||||
[Accept: "application/activity+json"],
|
||||
follow_redirect: true,
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000
|
||||
[{:Accept, "application/activity+json"}]
|
||||
),
|
||||
{:ok, data} <- Jason.decode(body),
|
||||
:ok <- Transmogrifier.contain_origin_from_id(id, data) do
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
defmodule Pleroma.Web.ActivityPub.MRF.EnsureRePrepended do
|
||||
alias Pleroma.Object
|
||||
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||
|
||||
@reply_prefix Regex.compile!("^re:[[:space:]]*", [:caseless])
|
||||
def filter_by_summary(
|
||||
%{"summary" => parent_summary} = parent,
|
||||
%{"summary" => child_summary} = child
|
||||
)
|
||||
when not is_nil(child_summary) and byte_size(child_summary) > 0 and
|
||||
not is_nil(parent_summary) and byte_size(parent_summary) > 0 do
|
||||
if (child_summary == parent_summary and not Regex.match?(@reply_prefix, child_summary)) or
|
||||
(Regex.match?(@reply_prefix, parent_summary) &&
|
||||
Regex.replace(@reply_prefix, parent_summary, "") == child_summary) do
|
||||
Map.put(child, "summary", "re: " <> child_summary)
|
||||
else
|
||||
child
|
||||
end
|
||||
end
|
||||
|
||||
def filter_by_summary(parent, child), do: child
|
||||
|
||||
def filter(%{"type" => activity_type} = object) when activity_type == "Create" do
|
||||
child = object["object"]
|
||||
in_reply_to = Object.normalize(child["inReplyTo"])
|
||||
|
||||
child =
|
||||
if(in_reply_to,
|
||||
do: filter_by_summary(in_reply_to.data, child),
|
||||
else: child
|
||||
)
|
||||
|
||||
object = Map.put(object, "object", child)
|
||||
|
||||
{:ok, object}
|
||||
end
|
||||
|
||||
def filter(object), do: {:ok, object}
|
||||
end
|
|
@ -82,7 +82,7 @@ def render("following.json", %{user: user, page: page}) do
|
|||
query = from(user in query, select: [:ap_id])
|
||||
following = Repo.all(query)
|
||||
|
||||
collection(following, "#{user.ap_id}/following", page)
|
||||
collection(following, "#{user.ap_id}/following", page, !user.info.hide_network)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
@ -95,7 +95,7 @@ def render("following.json", %{user: user}) do
|
|||
"id" => "#{user.ap_id}/following",
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => length(following),
|
||||
"first" => collection(following, "#{user.ap_id}/following", 1)
|
||||
"first" => collection(following, "#{user.ap_id}/following", 1, !user.info.hide_network)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
@ -105,7 +105,7 @@ def render("followers.json", %{user: user, page: page}) do
|
|||
query = from(user in query, select: [:ap_id])
|
||||
followers = Repo.all(query)
|
||||
|
||||
collection(followers, "#{user.ap_id}/followers", page)
|
||||
collection(followers, "#{user.ap_id}/followers", page, !user.info.hide_network)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
@ -118,7 +118,7 @@ def render("followers.json", %{user: user}) do
|
|||
"id" => "#{user.ap_id}/followers",
|
||||
"type" => "OrderedCollection",
|
||||
"totalItems" => length(followers),
|
||||
"first" => collection(followers, "#{user.ap_id}/followers", 1)
|
||||
"first" => collection(followers, "#{user.ap_id}/followers", 1, !user.info.hide_network)
|
||||
}
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
@ -172,7 +172,7 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
|
|||
end
|
||||
end
|
||||
|
||||
def collection(collection, iri, page, total \\ nil) do
|
||||
def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
||||
offset = (page - 1) * 10
|
||||
items = Enum.slice(collection, offset, 10)
|
||||
items = Enum.map(items, fn user -> user.ap_id end)
|
||||
|
@ -183,7 +183,7 @@ def collection(collection, iri, page, total \\ nil) do
|
|||
"type" => "OrderedCollectionPage",
|
||||
"partOf" => iri,
|
||||
"totalItems" => total,
|
||||
"orderedItems" => items
|
||||
"orderedItems" => if(show_items, do: items, else: [])
|
||||
}
|
||||
|
||||
if offset < total do
|
||||
|
|
|
@ -2,13 +2,22 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|
|||
use Pleroma.Web, :controller
|
||||
alias Pleroma.{Repo, Object, Activity, User, Notification, Stats}
|
||||
alias Pleroma.Web
|
||||
alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView, FilterView}
|
||||
|
||||
alias Pleroma.Web.MastodonAPI.{
|
||||
StatusView,
|
||||
AccountView,
|
||||
MastodonView,
|
||||
ListView,
|
||||
FilterView,
|
||||
PushSubscriptionView
|
||||
}
|
||||
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.OAuth.{Authorization, Token, App}
|
||||
alias Pleroma.Web.MediaProxy
|
||||
alias Comeonin.Pbkdf2
|
||||
|
||||
import Ecto.Query
|
||||
require Logger
|
||||
|
||||
|
@ -433,33 +442,31 @@ def relationships(%{assigns: %{user: user}} = conn, _) do
|
|||
|> json([])
|
||||
end
|
||||
|
||||
def update_media(%{assigns: %{user: _}} = conn, data) do
|
||||
def update_media(%{assigns: %{user: user}} = conn, data) do
|
||||
with %Object{} = object <- Repo.get(Object, data["id"]),
|
||||
true <- Object.authorize_mutation(object, user),
|
||||
true <- is_binary(data["description"]),
|
||||
description <- data["description"] do
|
||||
new_data = %{object.data | "name" => description}
|
||||
|
||||
change = Object.change(object, %{data: new_data})
|
||||
{:ok, _} = Repo.update(change)
|
||||
{:ok, _} =
|
||||
object
|
||||
|> Object.change(%{data: new_data})
|
||||
|> Repo.update()
|
||||
|
||||
data =
|
||||
new_data
|
||||
|> Map.put("id", object.id)
|
||||
|
||||
render(conn, StatusView, "attachment.json", %{attachment: data})
|
||||
attachment_data = Map.put(new_data, "id", object.id)
|
||||
render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
|
||||
end
|
||||
end
|
||||
|
||||
def upload(%{assigns: %{user: _}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <- ActivityPub.upload(file, description: Map.get(data, "description")) do
|
||||
change = Object.change(object, %{data: object.data})
|
||||
{:ok, object} = Repo.update(change)
|
||||
|
||||
objdata =
|
||||
object.data
|
||||
|> Map.put("id", object.id)
|
||||
|
||||
render(conn, StatusView, "attachment.json", %{attachment: objdata})
|
||||
def upload(%{assigns: %{user: user}} = conn, %{"file" => file} = data) do
|
||||
with {:ok, object} <-
|
||||
ActivityPub.upload(file,
|
||||
actor: User.ap_id(user),
|
||||
description: Map.get(data, "description")
|
||||
) do
|
||||
attachment_data = Map.put(object.data, "id", object.id)
|
||||
render(conn, StatusView, "attachment.json", %{attachment: attachment_data})
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -502,17 +509,30 @@ def hashtag_timeline(%{assigns: %{user: user}} = conn, params) do
|
|||
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
|
||||
end
|
||||
|
||||
# TODO: Pagination
|
||||
def followers(conn, %{"id" => id}) do
|
||||
def followers(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||
with %User{} = user <- Repo.get(User, id),
|
||||
{:ok, followers} <- User.get_followers(user) do
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> followers
|
||||
user.info.hide_network -> []
|
||||
true -> followers
|
||||
end
|
||||
|
||||
render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
|
||||
end
|
||||
end
|
||||
|
||||
def following(conn, %{"id" => id}) do
|
||||
def following(%{assigns: %{user: for_user}} = conn, %{"id" => id}) do
|
||||
with %User{} = user <- Repo.get(User, id),
|
||||
{:ok, followers} <- User.get_friends(user) do
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> followers
|
||||
user.info.hide_network -> []
|
||||
true -> followers
|
||||
end
|
||||
|
||||
render(conn, AccountView, "accounts.json", %{users: followers, as: :user})
|
||||
end
|
||||
end
|
||||
|
@ -959,9 +979,11 @@ def index(%{assigns: %{user: user}} = conn, _params) do
|
|||
end
|
||||
|
||||
def put_settings(%{assigns: %{user: user}} = conn, %{"data" => settings} = _params) do
|
||||
with new_info <- Map.put(user.info, "settings", settings),
|
||||
change <- User.info_changeset(user, %{info: new_info}),
|
||||
{:ok, _user} <- User.update_and_set_cache(change) do
|
||||
info_cng = User.Info.mastodon_settings_update(user.info, settings)
|
||||
|
||||
with changeset <- User.update_changeset(user),
|
||||
changeset <- Ecto.Changeset.put_embed(changeset, :info, info_cng),
|
||||
{:ok, user} <- User.update_and_set_cache(changeset) do
|
||||
conn
|
||||
|> json(%{})
|
||||
else
|
||||
|
@ -1149,6 +1171,33 @@ def delete_filter(%{assigns: %{user: user}} = conn, %{"id" => filter_id}) do
|
|||
json(conn, %{})
|
||||
end
|
||||
|
||||
def create_push_subscription(%{assigns: %{user: user, token: token}} = conn, params) do
|
||||
Pleroma.Web.Push.Subscription.delete_if_exists(user, token)
|
||||
{:ok, subscription} = Pleroma.Web.Push.Subscription.create(user, token, params)
|
||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
|
||||
def get_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||
subscription = Pleroma.Web.Push.Subscription.get(user, token)
|
||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
|
||||
def update_push_subscription(
|
||||
%{assigns: %{user: user, token: token}} = conn,
|
||||
params
|
||||
) do
|
||||
{:ok, subscription} = Pleroma.Web.Push.Subscription.update(user, token, params)
|
||||
view = PushSubscriptionView.render("push_subscription.json", subscription: subscription)
|
||||
json(conn, view)
|
||||
end
|
||||
|
||||
def delete_push_subscription(%{assigns: %{user: user, token: token}} = conn, _params) do
|
||||
{:ok, _response} = Pleroma.Web.Push.Subscription.delete(user, token)
|
||||
json(conn, %{})
|
||||
end
|
||||
|
||||
def errors(conn, _) do
|
||||
conn
|
||||
|> put_status(500)
|
||||
|
@ -1169,7 +1218,14 @@ def suggestions(%{assigns: %{user: user}} = conn, _) do
|
|||
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
|
||||
|
||||
with {:ok, %{status: 200, body: body}} <-
|
||||
@httpoison.get(url, [], timeout: timeout, recv_timeout: timeout),
|
||||
@httpoison.get(
|
||||
url,
|
||||
[],
|
||||
adapter: [
|
||||
timeout: timeout,
|
||||
recv_timeout: timeout
|
||||
]
|
||||
),
|
||||
{:ok, data} <- Jason.decode(body) do
|
||||
data2 =
|
||||
Enum.slice(data, 0, limit)
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
defmodule Pleroma.Web.MastodonAPI.PushSubscriptionView do
|
||||
use Pleroma.Web, :view
|
||||
alias Pleroma.Web.MastodonAPI.PushSubscriptionView
|
||||
|
||||
def render("push_subscription.json", %{subscription: subscription}) do
|
||||
%{
|
||||
id: to_string(subscription.id),
|
||||
endpoint: subscription.endpoint,
|
||||
alerts: Map.get(subscription.data, "alerts")
|
||||
}
|
||||
end
|
||||
end
|
|
@ -349,12 +349,7 @@ def fetch_activity_from_atom_url(url) do
|
|||
{:ok, %{body: body, status: code}} when code in 200..299 <-
|
||||
@httpoison.get(
|
||||
url,
|
||||
[Accept: "application/atom+xml"],
|
||||
follow_redirect: true,
|
||||
adapter: [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000
|
||||
]
|
||||
[{:Accept, "application/atom+xml"}]
|
||||
) do
|
||||
Logger.debug("Got document from #{url}, handling...")
|
||||
handle_incoming(body)
|
||||
|
@ -369,8 +364,7 @@ def fetch_activity_from_html_url(url) do
|
|||
Logger.debug("Trying to fetch #{url}")
|
||||
|
||||
with true <- String.starts_with?(url, "http"),
|
||||
{:ok, %{body: body}} <-
|
||||
@httpoison.get(url, [], follow_redirect: true, timeout: 10000, recv_timeout: 20000),
|
||||
{:ok, %{body: body}} <- @httpoison.get(url, []),
|
||||
{:ok, atom_url} <- get_atom_url(body) do
|
||||
fetch_activity_from_atom_url(atom_url)
|
||||
else
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
defmodule Pleroma.Web.Push do
|
||||
use GenServer
|
||||
|
||||
alias Pleroma.{Repo, User}
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
|
||||
require Logger
|
||||
import Ecto.Query
|
||||
|
||||
@types ["Create", "Follow", "Announce", "Like"]
|
||||
|
||||
@gcm_api_key nil
|
||||
|
||||
def start_link() do
|
||||
GenServer.start_link(__MODULE__, :ok, name: __MODULE__)
|
||||
end
|
||||
|
||||
def init(:ok) do
|
||||
case Application.get_env(:web_push_encryption, :vapid_details) do
|
||||
nil ->
|
||||
Logger.warn(
|
||||
"VAPID key pair is not found. Please, add VAPID configuration to config. Run `mix web_push.gen.keypair` mix task to create a key pair"
|
||||
)
|
||||
|
||||
:ignore
|
||||
|
||||
_ ->
|
||||
{:ok, %{}}
|
||||
end
|
||||
end
|
||||
|
||||
def send(notification) do
|
||||
if Application.get_env(:web_push_encryption, :vapid_details) do
|
||||
GenServer.cast(Pleroma.Web.Push, {:send, notification})
|
||||
end
|
||||
end
|
||||
|
||||
def handle_cast(
|
||||
{:send, %{activity: %{data: %{"type" => type}}, user_id: user_id} = notification},
|
||||
state
|
||||
)
|
||||
when type in @types do
|
||||
actor = User.get_cached_by_ap_id(notification.activity.data["actor"])
|
||||
body = notification |> format(actor) |> Jason.encode!()
|
||||
|
||||
Subscription
|
||||
|> where(user_id: ^user_id)
|
||||
|> Repo.all()
|
||||
|> Enum.each(fn record ->
|
||||
subscription = %{
|
||||
keys: %{
|
||||
p256dh: record.key_p256dh,
|
||||
auth: record.key_auth
|
||||
},
|
||||
endpoint: record.endpoint
|
||||
}
|
||||
|
||||
case WebPushEncryption.send_web_push(body, subscription, @gcm_api_key) do
|
||||
{:ok, %{status_code: code}} when 400 <= code and code < 500 ->
|
||||
Logger.debug("Removing subscription record")
|
||||
Repo.delete!(record)
|
||||
:ok
|
||||
|
||||
{:ok, %{status_code: code}} when 200 <= code and code < 300 ->
|
||||
:ok
|
||||
|
||||
{:ok, %{status_code: code}} ->
|
||||
Logger.error("Web Push Nonification failed with code: #{code}")
|
||||
:error
|
||||
|
||||
_ ->
|
||||
Logger.error("Web Push Nonification failed with unknown error")
|
||||
:error
|
||||
end
|
||||
end)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def handle_cast({:send, _}, state) do
|
||||
Logger.warn("Unknown notification type")
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
def format(%{activity: %{data: %{"type" => "Create"}}}, actor) do
|
||||
%{
|
||||
title: "New Mention",
|
||||
body: "@#{actor.nickname} has mentiond you",
|
||||
icon: User.avatar_url(actor)
|
||||
}
|
||||
end
|
||||
|
||||
def format(%{activity: %{data: %{"type" => "Follow"}}}, actor) do
|
||||
%{
|
||||
title: "New Follower",
|
||||
body: "@#{actor.nickname} has followed you",
|
||||
icon: User.avatar_url(actor)
|
||||
}
|
||||
end
|
||||
|
||||
def format(%{activity: %{data: %{"type" => "Announce"}}}, actor) do
|
||||
%{
|
||||
title: "New Announce",
|
||||
body: "@#{actor.nickname} has announced your post",
|
||||
icon: User.avatar_url(actor)
|
||||
}
|
||||
end
|
||||
|
||||
def format(%{activity: %{data: %{"type" => "Like"}}}, actor) do
|
||||
%{
|
||||
title: "New Like",
|
||||
body: "@#{actor.nickname} has liked your post",
|
||||
icon: User.avatar_url(actor)
|
||||
}
|
||||
end
|
||||
end
|
|
@ -0,0 +1,66 @@
|
|||
defmodule Pleroma.Web.Push.Subscription do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
alias Pleroma.{Repo, User}
|
||||
alias Pleroma.Web.OAuth.Token
|
||||
alias Pleroma.Web.Push.Subscription
|
||||
|
||||
schema "push_subscriptions" do
|
||||
belongs_to(:user, User)
|
||||
belongs_to(:token, Token)
|
||||
field(:endpoint, :string)
|
||||
field(:key_p256dh, :string)
|
||||
field(:key_auth, :string)
|
||||
field(:data, :map, default: %{})
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
@supported_alert_types ~w[follow favourite mention reblog]
|
||||
|
||||
defp alerts(%{"data" => %{"alerts" => alerts}}) do
|
||||
alerts = Map.take(alerts, @supported_alert_types)
|
||||
%{"alerts" => alerts}
|
||||
end
|
||||
|
||||
def create(
|
||||
%User{} = user,
|
||||
%Token{} = token,
|
||||
%{
|
||||
"subscription" => %{
|
||||
"endpoint" => endpoint,
|
||||
"keys" => %{"auth" => key_auth, "p256dh" => key_p256dh}
|
||||
}
|
||||
} = params
|
||||
) do
|
||||
Repo.insert(%Subscription{
|
||||
user_id: user.id,
|
||||
token_id: token.id,
|
||||
endpoint: endpoint,
|
||||
key_auth: key_auth,
|
||||
key_p256dh: key_p256dh,
|
||||
data: alerts(params)
|
||||
})
|
||||
end
|
||||
|
||||
def get(%User{id: user_id}, %Token{id: token_id}) do
|
||||
Repo.get_by(Subscription, user_id: user_id, token_id: token_id)
|
||||
end
|
||||
|
||||
def update(user, token, params) do
|
||||
get(user, token)
|
||||
|> change(data: alerts(params))
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
def delete(user, token) do
|
||||
Repo.delete(get(user, token))
|
||||
end
|
||||
|
||||
def delete_if_exists(user, token) do
|
||||
case get(user, token) do
|
||||
nil -> {:ok, nil}
|
||||
sub -> Repo.delete(sub)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -198,6 +198,11 @@ defmodule Pleroma.Web.Router do
|
|||
put("/filters/:id", MastodonAPIController, :update_filter)
|
||||
delete("/filters/:id", MastodonAPIController, :delete_filter)
|
||||
|
||||
post("/push/subscription", MastodonAPIController, :create_push_subscription)
|
||||
get("/push/subscription", MastodonAPIController, :get_push_subscription)
|
||||
put("/push/subscription", MastodonAPIController, :update_push_subscription)
|
||||
delete("/push/subscription", MastodonAPIController, :delete_push_subscription)
|
||||
|
||||
get("/suggestions", MastodonAPIController, :suggestions)
|
||||
|
||||
get("/endorsements", MastodonAPIController, :empty_array)
|
||||
|
@ -324,6 +329,7 @@ defmodule Pleroma.Web.Router do
|
|||
|
||||
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)
|
||||
|
|
|
@ -162,12 +162,7 @@ defp send_to_user(%{info: %{salmon: salmon}}, feed, poster) do
|
|||
poster.(
|
||||
salmon,
|
||||
feed,
|
||||
[{"Content-Type", "application/magic-envelope+xml"}],
|
||||
adapter: [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
pool: :default
|
||||
]
|
||||
[{"Content-Type", "application/magic-envelope+xml"}]
|
||||
) do
|
||||
Logger.debug(fn -> "Pushed to #{salmon}, code #{code}" end)
|
||||
else
|
||||
|
|
|
@ -157,13 +157,17 @@ def config(conn, _params) do
|
|||
|> send_resp(200, response)
|
||||
|
||||
_ ->
|
||||
vapid_public_key =
|
||||
Keyword.get(Application.get_env(:web_push_encryption, :vapid_details), :public_key)
|
||||
|
||||
data = %{
|
||||
name: Keyword.get(instance, :name),
|
||||
description: Keyword.get(instance, :description),
|
||||
server: Web.base_url(),
|
||||
textlimit: to_string(Keyword.get(instance, :limit)),
|
||||
closed: if(Keyword.get(instance, :registrations_open), do: "0", else: "1"),
|
||||
private: if(Keyword.get(instance, :public, true), do: "0", else: "1")
|
||||
private: if(Keyword.get(instance, :public, true), do: "0", else: "1"),
|
||||
vapidPublicKey: vapid_public_key
|
||||
}
|
||||
|
||||
pleroma_fe = %{
|
||||
|
|
|
@ -93,8 +93,8 @@ def unfav(%User{} = user, ap_id_or_id) do
|
|||
end
|
||||
end
|
||||
|
||||
def upload(%Plug.Upload{} = file, format \\ "xml") do
|
||||
{:ok, object} = ActivityPub.upload(file)
|
||||
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"]
|
||||
|
|
|
@ -4,7 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
|
|||
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
|
||||
alias Pleroma.{Repo, Activity, User, Notification}
|
||||
alias Pleroma.{Repo, Activity, Object, User, Notification}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Utils
|
||||
alias Ecto.Changeset
|
||||
|
@ -226,16 +226,51 @@ def fetch_conversation(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
|||
end
|
||||
end
|
||||
|
||||
def upload(conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media)
|
||||
@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(conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media, "json")
|
||||
def upload_json(%{assigns: %{user: user}} = conn, %{"media" => media}) do
|
||||
response = TwitterAPI.upload(media, user, "json")
|
||||
|
||||
conn
|
||||
|> json_reply(200, response)
|
||||
|
@ -340,18 +375,32 @@ def external_profile(%{assigns: %{user: current_user}} = conn, %{"profileurl" =>
|
|||
end
|
||||
end
|
||||
|
||||
def followers(conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
||||
def followers(%{assigns: %{user: for_user}} = conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(for_user, params),
|
||||
{:ok, followers} <- User.get_followers(user) do
|
||||
followers =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> followers
|
||||
user.info.hide_network -> []
|
||||
true -> followers
|
||||
end
|
||||
|
||||
render(conn, UserView, "index.json", %{users: followers, for: conn.assigns[:user]})
|
||||
else
|
||||
_e -> bad_request_reply(conn, "Can't get followers")
|
||||
end
|
||||
end
|
||||
|
||||
def friends(conn, params) do
|
||||
def friends(%{assigns: %{user: for_user}} = conn, params) do
|
||||
with {:ok, user} <- TwitterAPI.get_user(conn.assigns[:user], params),
|
||||
{:ok, friends} <- User.get_friends(user) do
|
||||
friends =
|
||||
cond do
|
||||
for_user && user.id == for_user.id -> friends
|
||||
user.info.hide_network -> []
|
||||
true -> friends
|
||||
end
|
||||
|
||||
render(conn, UserView, "index.json", %{users: friends, for: conn.assigns[:user]})
|
||||
else
|
||||
_e -> bad_request_reply(conn, "Can't get friends")
|
||||
|
@ -429,7 +478,7 @@ def raw_empty_array(conn, _params) do
|
|||
|
||||
defp build_info_cng(user, params) do
|
||||
info_params =
|
||||
["no_rich_text", "locked"]
|
||||
["no_rich_text", "locked", "hide_network"]
|
||||
|> Enum.reduce(%{}, fn key, res ->
|
||||
if value = params[key] do
|
||||
Map.put(res, key, value == "true")
|
||||
|
|
|
@ -221,7 +221,7 @@ def get_template_from_xml(body) do
|
|||
|
||||
def find_lrdd_template(domain) do
|
||||
with {:ok, %{status: status, body: body}} when status in 200..299 <-
|
||||
@httpoison.get("http://#{domain}/.well-known/host-meta", [], follow_redirect: true) do
|
||||
@httpoison.get("http://#{domain}/.well-known/host-meta", []) do
|
||||
get_template_from_xml(body)
|
||||
else
|
||||
_ ->
|
||||
|
|
|
@ -264,11 +264,6 @@ def publish_one(%{xml: xml, topic: topic, callback: callback, secret: secret}) d
|
|||
[
|
||||
{"Content-Type", "application/atom+xml"},
|
||||
{"X-Hub-Signature", "sha1=#{signature}"}
|
||||
],
|
||||
adapter: [
|
||||
timeout: 10000,
|
||||
recv_timeout: 20000,
|
||||
pool: :default
|
||||
]
|
||||
) do
|
||||
Logger.info(fn -> "Pushed to #{callback}, code #{code}" end)
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -68,7 +68,8 @@ defp deps do
|
|||
{:crypt,
|
||||
git: "https://github.com/msantos/crypt", ref: "1f2b58927ab57e72910191a7ebaeff984382a1d3"},
|
||||
{:cors_plug, "~> 1.5"},
|
||||
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false}
|
||||
{:ex_doc, "> 0.18.3 and < 0.20.0", only: :dev, runtime: false},
|
||||
{:web_push_encryption, "~> 0.2.1"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
3
mix.lock
3
mix.lock
|
@ -1,4 +1,5 @@
|
|||
%{
|
||||
"base64url": {:hex, :base64url, "0.0.1", "36a90125f5948e3afd7be97662a1504b934dd5dac78451ca6e9abf85a10286be", [:rebar], [], "hexpm"},
|
||||
"bunt": {:hex, :bunt, "0.2.0", "951c6e801e8b1d2cbe58ebbd3e616a869061ddadcc4863d0a2182541acae9a38", [:mix], [], "hexpm"},
|
||||
"cachex": {:hex, :cachex, "3.0.2", "1351caa4e26e29f7d7ec1d29b53d6013f0447630bbf382b4fb5d5bad0209f203", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"calendar": {:hex, :calendar, "0.17.4", "22c5e8d98a4db9494396e5727108dffb820ee0d18fed4b0aa8ab76e4f5bc32f1", [:mix], [{:tzdata, "~> 0.5.8 or ~> 0.1.201603", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -25,6 +26,7 @@
|
|||
"httpoison": {:hex, :httpoison, "1.2.0", "2702ed3da5fd7a8130fc34b11965c8cfa21ade2f232c00b42d96d4967c39a3a3", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "5.1.2", "e21cb58a09f0228a9e0b95eaa1217f1bcfc31a1aaa6e1fdf2f53a33f7dbd9494", [:rebar3], [{:unicode_util_compat, "0.3.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"jose": {:hex, :jose, "1.8.4", "7946d1e5c03a76ac9ef42a6e6a20001d35987afd68c2107bcd8f01a84e75aa73", [:mix, :rebar3], [{:base64url, "~> 0.0.1", [hex: :base64url, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup": {:hex, :makeup, "0.5.5", "9e08dfc45280c5684d771ad58159f718a7b5788596099bdfb0284597d368a882", [:mix], [{:nimble_parsec, "~> 0.4", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.10.0", "0f09c2ddf352887a956d84f8f7e702111122ca32fbbc84c2f0569b8b65cbf7fa", [:mix], [{:makeup, "~> 0.5.5", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"meck": {:hex, :meck, "0.8.9", "64c5c0bd8bcca3a180b44196265c8ed7594e16bcc845d0698ec6b4e577f48188", [:rebar3], [], "hexpm"},
|
||||
|
@ -52,4 +54,5 @@
|
|||
"tzdata": {:hex, :tzdata, "0.5.17", "50793e3d85af49736701da1a040c415c97dc1caf6464112fd9bd18f425d3053b", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.3.1", "a1f612a7b512638634a603c8f401892afbf99b8ce93a45041f8aaca99cadb85e", [:rebar3], [], "hexpm"},
|
||||
"unsafe": {:hex, :unsafe, "1.0.0", "7c21742cd05380c7875546b023481d3a26f52df8e5dfedcb9f958f322baae305", [:mix], [], "hexpm"},
|
||||
"web_push_encryption": {:hex, :web_push_encryption, "0.2.1", "d42cecf73420d9dc0053ba3299cc8c8d6ff2be2487d67ca2a57265868e4d9a98", [:mix], [{:httpoison, "~> 1.0", [hex: :httpoison, repo: "hexpm", optional: false]}, {:jose, "~> 1.8", [hex: :jose, repo: "hexpm", optional: false]}, {:poison, "~> 3.0", [hex: :poison, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
defmodule Pleroma.Repo.Migrations.CreatePushSubscriptions do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
create table("push_subscriptions") do
|
||||
add :user_id, references("users", on_delete: :delete_all)
|
||||
add :token_id, references("oauth_tokens", on_delete: :delete_all)
|
||||
add :endpoint, :string
|
||||
add :key_p256dh, :string
|
||||
add :key_auth, :string
|
||||
add :data, :map
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
create index("push_subscriptions", [:user_id, :token_id], unique: true)
|
||||
end
|
||||
end
|
|
@ -15,7 +15,7 @@ test "turns hashtags into links" do
|
|||
text = "I love #cofe and #2hu"
|
||||
|
||||
expected_text =
|
||||
"I love <a href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
|
||||
"I love <a data-tag='cofe' href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
|
||||
|
||||
tags = Formatter.parse_tags(text)
|
||||
|
||||
|
@ -128,11 +128,11 @@ test "gives a replacement for user links" do
|
|||
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
|
||||
|
||||
expected_text =
|
||||
"<span><a class='mention' href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a class='mention' href='#{
|
||||
"https://archeme/@archaeme"
|
||||
}'>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a class='mention' href='#{
|
||||
archaeme_remote.ap_id
|
||||
}'>@<span>archaeme</span></a></span>"
|
||||
"<span><a data-user='#{gsimg.id}' class='mention' href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a data-user='#{
|
||||
archaeme.id
|
||||
}' class='mention' href='#{"https://archeme/@archaeme"}'>@<span>archaeme</span></a></span>, that is @daggsy. Also hello <span><a data-user='#{
|
||||
archaeme_remote.id
|
||||
}' class='mention' href='#{archaeme_remote.ap_id}'>@<span>archaeme</span></a></span>"
|
||||
|
||||
assert expected_text == Formatter.finalize({subs, text})
|
||||
end
|
||||
|
@ -150,7 +150,7 @@ test "gives a replacement for user links when the user is using Osada" do
|
|||
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
|
||||
|
||||
expected_text =
|
||||
"<span><a class='mention' href='#{mike.ap_id}'>@<span>mike</span></a></span> test"
|
||||
"<span><a data-user='#{mike.id}' class='mention' href='#{mike.ap_id}'>@<span>mike</span></a></span> test"
|
||||
|
||||
assert expected_text == Formatter.finalize({subs, text})
|
||||
end
|
||||
|
@ -166,7 +166,9 @@ test "gives a replacement for single-character local nicknames" do
|
|||
assert length(subs) == 1
|
||||
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
|
||||
|
||||
expected_text = "<span><a class='mention' href='#{o.ap_id}'>@<span>o</span></a></span> hi"
|
||||
expected_text =
|
||||
"<span><a data-user='#{o.id}' class='mention' href='#{o.ap_id}'>@<span>o</span></a></span> hi"
|
||||
|
||||
assert expected_text == Formatter.finalize({subs, text})
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
defmodule Pleroma.Plugs.OAuthPlugTest do
|
||||
use Pleroma.Web.ConnCase, async: true
|
||||
|
||||
alias Pleroma.Plugs.OAuthPlug
|
||||
import Pleroma.Factory
|
||||
|
||||
@session_opts [
|
||||
store: :cookie,
|
||||
key: "_test",
|
||||
signing_salt: "cooldude"
|
||||
]
|
||||
|
||||
setup %{conn: conn} do
|
||||
user = insert(:user)
|
||||
{:ok, %{token: token}} = Pleroma.Web.OAuth.Token.create_token(insert(:oauth_app), user)
|
||||
%{user: user, token: token, conn: conn}
|
||||
end
|
||||
|
||||
test "with valid token(uppercase), it assigns the user", %{conn: conn} = opts do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "BEARER #{opts[:token]}")
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user] == opts[:user]
|
||||
end
|
||||
|
||||
test "with valid token(downcase), it assigns the user", %{conn: conn} = opts do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "bearer #{opts[:token]}")
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user] == opts[:user]
|
||||
end
|
||||
|
||||
test "with invalid token, it not assigns the user", %{conn: conn} do
|
||||
conn =
|
||||
conn
|
||||
|> put_req_header("authorization", "bearer TTTTT")
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
refute conn.assigns[:user]
|
||||
end
|
||||
|
||||
test "when token is missed but token in session, it assigns the user", %{conn: conn} = opts do
|
||||
conn =
|
||||
conn
|
||||
|> Plug.Session.call(Plug.Session.init(@session_opts))
|
||||
|> fetch_session()
|
||||
|> put_session(:oauth_token, opts[:token])
|
||||
|> OAuthPlug.call(%{})
|
||||
|
||||
assert conn.assigns[:user] == opts[:user]
|
||||
end
|
||||
end
|
|
@ -36,6 +36,23 @@ defmodule Pleroma.DataCase do
|
|||
:ok
|
||||
end
|
||||
|
||||
def ensure_local_uploader(_context) do
|
||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||
filters = Pleroma.Config.get([Pleroma.Upload, :filters])
|
||||
|
||||
unless uploader == Pleroma.Uploaders.Local || filters != [] do
|
||||
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||
Pleroma.Config.put([Pleroma.Upload, :filters], [])
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([Pleroma.Upload, :uploader], uploader)
|
||||
Pleroma.Config.put([Pleroma.Upload, :filters], filters)
|
||||
end)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
@doc """
|
||||
A helper that transform changeset errors to a map of messages.
|
||||
|
||||
|
|
|
@ -3,22 +3,7 @@ defmodule Pleroma.UploadTest do
|
|||
use Pleroma.DataCase
|
||||
|
||||
describe "Storing a file with the Local uploader" do
|
||||
setup do
|
||||
uploader = Pleroma.Config.get([Pleroma.Upload, :uploader])
|
||||
filters = Pleroma.Config.get([Pleroma.Upload, :filters])
|
||||
|
||||
unless uploader == Pleroma.Uploaders.Local || filters != [] do
|
||||
Pleroma.Config.put([Pleroma.Upload, :uploader], Pleroma.Uploaders.Local)
|
||||
Pleroma.Config.put([Pleroma.Upload, :filters], [])
|
||||
|
||||
on_exit(fn ->
|
||||
Pleroma.Config.put([Pleroma.Upload, :uploader], uploader)
|
||||
Pleroma.Config.put([Pleroma.Upload, :filters], filters)
|
||||
end)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
setup [:ensure_local_uploader]
|
||||
|
||||
test "returns a media url" do
|
||||
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
|
||||
|
|
|
@ -150,6 +150,20 @@ test "it returns the followers in a collection", %{conn: conn} do
|
|||
assert result["first"]["orderedItems"] == [user.ap_id]
|
||||
end
|
||||
|
||||
test "it returns returns empty if the user has 'hide_network' set", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
user_two = insert(:user, %{info: %{hide_network: true}})
|
||||
User.follow(user, user_two)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> get("/users/#{user_two.nickname}/followers")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["first"]["orderedItems"] == []
|
||||
assert result["totalItems"] == 1
|
||||
end
|
||||
|
||||
test "it works for more than 10 users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
@ -191,6 +205,20 @@ test "it returns the following in a collection", %{conn: conn} do
|
|||
assert result["first"]["orderedItems"] == [user_two.ap_id]
|
||||
end
|
||||
|
||||
test "it returns returns empty if the user has 'hide_network' set", %{conn: conn} do
|
||||
user = insert(:user, %{info: %{hide_network: true}})
|
||||
user_two = insert(:user)
|
||||
User.follow(user, user_two)
|
||||
|
||||
result =
|
||||
conn
|
||||
|> get("/users/#{user.nickname}/following")
|
||||
|> json_response(200)
|
||||
|
||||
assert result["first"]["orderedItems"] == []
|
||||
assert result["totalItems"] == 1
|
||||
end
|
||||
|
||||
test "it works for more than 10 users", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIControllerTest do
|
|||
use Pleroma.Web.ConnCase
|
||||
|
||||
alias Pleroma.Web.TwitterAPI.TwitterAPI
|
||||
alias Pleroma.{Repo, User, Activity, Notification}
|
||||
alias Pleroma.{Repo, User, Object, Activity, Notification}
|
||||
alias Pleroma.Web.{OStatus, CommonAPI}
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
|
||||
|
@ -590,7 +590,7 @@ test "list of notifications", %{conn: conn} do
|
|||
|> get("/api/v1/notifications")
|
||||
|
||||
expected_response =
|
||||
"hi <span><a href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>"
|
||||
"hi <span><a data-user=\"#{user.id}\" href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>"
|
||||
|
||||
assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
|
||||
assert response == expected_response
|
||||
|
@ -611,7 +611,7 @@ test "getting a single notification", %{conn: conn} do
|
|||
|> get("/api/v1/notifications/#{notification.id}")
|
||||
|
||||
expected_response =
|
||||
"hi <span><a href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>"
|
||||
"hi <span><a data-user=\"#{user.id}\" href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>"
|
||||
|
||||
assert %{"status" => %{"content" => response}} = json_response(conn, 200)
|
||||
assert response == expected_response
|
||||
|
@ -810,7 +810,7 @@ test "gets an users media", %{conn: conn} do
|
|||
}
|
||||
|
||||
media =
|
||||
TwitterAPI.upload(file, "json")
|
||||
TwitterAPI.upload(file, user, "json")
|
||||
|> Poison.decode!()
|
||||
|
||||
{:ok, image_post} =
|
||||
|
@ -965,6 +965,10 @@ test "media upload", %{conn: conn} do
|
|||
|
||||
assert media["type"] == "image"
|
||||
assert media["description"] == desc
|
||||
assert media["id"]
|
||||
|
||||
object = Repo.get(Object, media["id"])
|
||||
assert object.data["actor"] == User.ap_id(user)
|
||||
end
|
||||
|
||||
test "hashtag timeline", %{conn: conn} do
|
||||
|
@ -1008,6 +1012,31 @@ test "getting followers", %{conn: conn} do
|
|||
assert id == to_string(user.id)
|
||||
end
|
||||
|
||||
test "getting followers, hide_network", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user, %{info: %{hide_network: true}})
|
||||
{:ok, user} = User.follow(user, other_user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/accounts/#{other_user.id}/followers")
|
||||
|
||||
assert [] == json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "getting followers, hide_network, same user requesting", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user, %{info: %{hide_network: true}})
|
||||
{:ok, user} = User.follow(user, other_user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, other_user)
|
||||
|> get("/api/v1/accounts/#{other_user.id}/followers")
|
||||
|
||||
refute [] == json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "getting following", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
@ -1021,6 +1050,31 @@ test "getting following", %{conn: conn} do
|
|||
assert id == to_string(other_user.id)
|
||||
end
|
||||
|
||||
test "getting following, hide_network", %{conn: conn} do
|
||||
user = insert(:user, %{info: %{hide_network: true}})
|
||||
other_user = insert(:user)
|
||||
{:ok, user} = User.follow(user, other_user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> get("/api/v1/accounts/#{user.id}/following")
|
||||
|
||||
assert [] == json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "getting following, hide_network, same user requesting", %{conn: conn} do
|
||||
user = insert(:user, %{info: %{hide_network: true}})
|
||||
other_user = insert(:user)
|
||||
{:ok, user} = User.follow(user, other_user)
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> get("/api/v1/accounts/#{user.id}/following")
|
||||
|
||||
refute [] == json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "following / unfollowing a user", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
other_user = insert(:user)
|
||||
|
@ -1271,9 +1325,9 @@ test "updates the user's bio", %{conn: conn} do
|
|||
assert user = json_response(conn, 200)
|
||||
|
||||
assert user["note"] ==
|
||||
"I drink <a href=\"http://localhost:4001/tag/cofe\">#cofe</a> with <span><a href=\"#{
|
||||
user2.ap_id
|
||||
}\">@<span>#{user2.nickname}</span></a></span>"
|
||||
"I drink <a data-tag=\"cofe\" href=\"http://localhost:4001/tag/cofe\">#cofe</a> with <span><a data-user=\"#{
|
||||
user2.id
|
||||
}\" href=\"#{user2.ap_id}\">@<span>#{user2.nickname}</span></a></span>"
|
||||
end
|
||||
|
||||
test "updates the user's locking status", %{conn: conn} do
|
||||
|
|
|
@ -861,6 +861,67 @@ test "it returns a user's followers", %{conn: conn} do
|
|||
result = json_response(conn, 200)
|
||||
assert Enum.sort(expected) == Enum.sort(result)
|
||||
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 for a hidden network", %{conn: conn} do
|
||||
user = insert(:user, %{info: %{hide_network: 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, not_follower)
|
||||
|> get("/api/statuses/followers", %{"user_id" => user.id})
|
||||
|
||||
assert [] == json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "it returns the followers for a hidden network if requested by the user themselves", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user, %{info: %{hide_network: 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/friends" do
|
||||
|
@ -905,6 +966,42 @@ test "it returns a given user's friends with user_id", %{conn: conn} do
|
|||
)
|
||||
end
|
||||
|
||||
test "it returns empty for a hidden network", %{conn: conn} do
|
||||
user = insert(:user, %{info: %{hide_network: 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 for a hidden network if the user themselves request it", %{
|
||||
conn: conn
|
||||
} do
|
||||
user = insert(:user, %{info: %{hide_network: 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, user)
|
||||
|> get("/api/statuses/friends", %{"user_id" => user.id})
|
||||
|
||||
refute [] == json_response(conn, 200)
|
||||
end
|
||||
|
||||
test "it returns a given user's friends with screen_name", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
followed_one = insert(:user)
|
||||
|
@ -969,11 +1066,37 @@ test "it updates a user's profile", %{conn: conn} do
|
|||
assert user.name == "new name"
|
||||
|
||||
assert user.bio ==
|
||||
"hi <span><a class='mention' href='#{user2.ap_id}'>@<span>#{user2.nickname}</span></a></span>"
|
||||
"hi <span><a data-user='#{user2.id}' class='mention' href='#{user2.ap_id}'>@<span>#{
|
||||
user2.nickname
|
||||
}</span></a></span>"
|
||||
|
||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
||||
end
|
||||
|
||||
test "it sets and un-sets hide_network", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/account/update_profile.json", %{
|
||||
"hide_network" => "true"
|
||||
})
|
||||
|
||||
user = Repo.get!(User, user.id)
|
||||
assert user.info.hide_network == true
|
||||
|
||||
conn =
|
||||
conn
|
||||
|> assign(:user, user)
|
||||
|> post("/api/account/update_profile.json", %{
|
||||
"hide_network" => "false"
|
||||
})
|
||||
|
||||
user = Repo.get!(User, user.id)
|
||||
assert user.info.hide_network == false
|
||||
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
|
||||
end
|
||||
|
||||
test "it locks an account", %{conn: conn} do
|
||||
user = insert(:user)
|
||||
|
||||
|
@ -1253,4 +1376,82 @@ test "it returns users, ordered by similarity", %{conn: conn} do
|
|||
assert [user.id, user_two.id, user_three.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_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
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPITest do
|
|||
|
||||
test "create a status" do
|
||||
user = insert(:user)
|
||||
_mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
|
||||
mentioned_user = insert(:user, %{nickname: "shp", ap_id: "shp"})
|
||||
|
||||
object_data = %{
|
||||
"type" => "Image",
|
||||
|
@ -35,7 +35,7 @@ test "create a status" do
|
|||
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
|
||||
|
||||
expected_text =
|
||||
"Hello again, <span><a class='mention' href='shp'>@<span>shp</span></a></span>.<script></script><br>This is on another :moominmamma: line. <a href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>"
|
||||
"Hello again, <span><a data-user='#{mentioned_user.id}' class='mention' href='shp'>@<span>shp</span></a></span>.<script></script><br>This is on another :moominmamma: line. <a data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a data-tag='phantasmagoric' href='http://localhost:4001/tag/phantasmagoric' rel='tag'>#phantasmagoric</a><br><a href=\"http://example.org/image.jpg\" class='attachment'>image.jpg</a>"
|
||||
|
||||
assert get_in(activity.data, ["object", "content"]) == expected_text
|
||||
assert get_in(activity.data, ["object", "type"]) == "Note"
|
||||
|
@ -182,13 +182,15 @@ test "Unblock another user using screen_name" do
|
|||
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)
|
||||
response = TwitterAPI.upload(file, user)
|
||||
|
||||
assert is_binary(response)
|
||||
end
|
||||
|
@ -281,7 +283,7 @@ test "it registers a new user and parses mentions in the bio" do
|
|||
{:ok, user2} = TwitterAPI.register_user(data2)
|
||||
|
||||
expected_text =
|
||||
"<span><a class='mention' href='#{user1.ap_id}'>@<span>john</span></a></span> test"
|
||||
"<span><a data-user='#{user1.id}' class='mention' href='#{user1.ap_id}'>@<span>john</span></a></span> test"
|
||||
|
||||
assert user2.bio == expected_text
|
||||
end
|
||||
|
|
|
@ -47,7 +47,7 @@ test "a create activity with a note" do
|
|||
"repeated" => false,
|
||||
"statusnet_conversation_id" => convo_id,
|
||||
"statusnet_html" =>
|
||||
"Hey <span><a href=\"#{other_user.ap_id}\">@<span>shp</span></a></span>!",
|
||||
"Hey <span><a data-user=\"#{other_user.id}\" href=\"#{other_user.ap_id}\">@<span>shp</span></a></span>!",
|
||||
"tags" => [],
|
||||
"text" => "Hey @shp!",
|
||||
"uri" => activity.data["object"]["id"],
|
||||
|
|
Loading…
Reference in New Issue