Merge branch 'develop' into kaniini/pleroma-bugfix/unlisted-statuses

This commit is contained in:
lain 2018-05-13 10:56:11 +02:00
commit 76722ea9c8
59 changed files with 472 additions and 194 deletions

View file

@ -26,3 +26,12 @@
config :pleroma, :websub, Pleroma.Web.WebsubMock config :pleroma, :websub, Pleroma.Web.WebsubMock
config :pleroma, :ostatus, Pleroma.Web.OStatusMock config :pleroma, :ostatus, Pleroma.Web.OStatusMock
config :pleroma, :httpoison, HTTPoisonMock config :pleroma, :httpoison, HTTPoisonMock
try do
import_config "test.secret.exs"
rescue
_ ->
IO.puts(
"You may want to create test.secret.exs to declare custom database connection parameters."
)
end

View file

@ -59,6 +59,16 @@ server {
} }
# stop removing lines here. # stop removing lines here.
add_header X-XSS-Protection "1; mode=block";
add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin;
add_header X-Download-Options noopen;
# Uncomment this only after you get HTTPS working.
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
proxy_http_version 1.1; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; proxy_set_header Connection "upgrade";

View file

@ -13,4 +13,3 @@ Restart=on-failure
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
Alias=pleroma.service

View file

@ -39,15 +39,9 @@ sub vcl_recv {
return (hash); return (hash);
} }
# Hack to enable a Terms of Service page missing from Pleroma
if (req.url ~ "^/about/more$") {
set req.http.x-redir = "https://" + req.http.host + "/static/terms-of-service.html";
return (synth(750, ""));
}
# Strip headers that will affect caching from all other static content # Strip headers that will affect caching from all other static content
# This also permits caching of individual toots and AP Activities # This also permits caching of individual toots and AP Activities
if ((req.url ~ "^/(media|notice|objects|static)/") || if ((req.url ~ "^/(media|static)/") ||
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$")) (req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
{ {
unset req.http.Cookie; unset req.http.Cookie;
@ -99,8 +93,7 @@ sub vcl_backend_response {
# Strip cache-restricting headers from Pleroma on static content that we want to cache # Strip cache-restricting headers from Pleroma on static content that we want to cache
# Also enable streaming of cached content to clients (no waiting for Varnish to complete backend fetch) # Also enable streaming of cached content to clients (no waiting for Varnish to complete backend fetch)
if ((bereq.url ~ "^/(notice|objects)/") || if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$")
(bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
{ {
unset beresp.http.set-cookie; unset beresp.http.set-cookie;
unset beresp.http.Cache-Control; unset beresp.http.Cache-Control;

View file

@ -1,6 +1,5 @@
defmodule Mix.Tasks.FixApUsers do defmodule Mix.Tasks.FixApUsers do
use Mix.Task use Mix.Task
import Mix.Ecto
import Ecto.Query import Ecto.Query
alias Pleroma.{Repo, User} alias Pleroma.{Repo, User}

View file

@ -1,7 +1,6 @@
defmodule Mix.Tasks.GeneratePasswordReset do defmodule Mix.Tasks.GeneratePasswordReset do
use Mix.Task use Mix.Task
import Mix.Ecto alias Pleroma.User
alias Pleroma.{Repo, User}
@shortdoc "Generate password reset link for user" @shortdoc "Generate password reset link for user"
def run([nickname]) do def run([nickname]) do

View file

@ -1,6 +1,5 @@
defmodule Mix.Tasks.RegisterUser do defmodule Mix.Tasks.RegisterUser do
use Mix.Task use Mix.Task
import Mix.Ecto
alias Pleroma.{Repo, User} alias Pleroma.{Repo, User}
@shortdoc "Register user" @shortdoc "Register user"

View file

@ -1,7 +1,6 @@
defmodule Mix.Tasks.RmUser do defmodule Mix.Tasks.RmUser do
use Mix.Task use Mix.Task
import Mix.Ecto alias Pleroma.User
alias Pleroma.{User, Repo}
@shortdoc "Permanently delete a user" @shortdoc "Permanently delete a user"
def run([nickname]) do def run([nickname]) do

View file

@ -23,6 +23,18 @@ def start(_type, _args) do
limit: 2500 limit: 2500
] ]
]), ]),
worker(
Cachex,
[
:idempotency_cache,
[
default_ttl: :timer.seconds(6 * 60 * 60),
ttl_interval: :timer.seconds(60),
limit: 2500
]
],
id: :cachex_idem
),
worker(Pleroma.Web.Federator, []), worker(Pleroma.Web.Federator, []),
worker(Pleroma.Gopher.Server, []), worker(Pleroma.Gopher.Server, []),
worker(Pleroma.Stats, []) worker(Pleroma.Stats, [])

View file

@ -65,12 +65,6 @@ def link(name, selector, type \\ 1) do
"#{type}#{name}\t#{selector}\t#{address}\t#{port}\r\n" "#{type}#{name}\t#{selector}\t#{address}\t#{port}\r\n"
end end
def response("") do
info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
link("Public Timeline", "/main/public") <>
link("Federated Timeline", "/main/all") <> ".\r\n"
end
def render_activities(activities) do def render_activities(activities) do
activities activities
|> Enum.reverse() |> Enum.reverse()
@ -93,6 +87,12 @@ def render_activities(activities) do
|> Enum.join("\r\n") |> Enum.join("\r\n")
end end
def response("") do
info("Welcome to #{Keyword.get(@instance, :name, "Pleroma")}!") <>
link("Public Timeline", "/main/public") <>
link("Federated Timeline", "/main/all") <> ".\r\n"
end
def response("/main/public") do def response("/main/public") do
posts = posts =
ActivityPub.fetch_public_activities(%{"type" => ["Create"], "local_only" => true}) ActivityPub.fetch_public_activities(%{"type" => ["Create"], "local_only" => true})

View file

@ -91,7 +91,8 @@ def create_notifications(_), do: {:ok, []}
# TODO move to sql, too. # TODO move to sql, too.
def create_notification(%Activity{} = activity, %User{} = user) do def create_notification(%Activity{} = activity, %User{} = user) do
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) do unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
user.ap_id == activity.data["actor"] do
notification = %Notification{user_id: user.id, activity: activity} notification = %Notification{user_id: user.id, activity: activity}
{:ok, notification} = Repo.insert(notification) {:ok, notification} = Repo.insert(notification)
Pleroma.Web.Streamer.stream("user", notification) Pleroma.Web.Streamer.stream("user", notification)

View file

@ -7,11 +7,11 @@ def init(options) do
options options
end end
def call(%{assigns: %{valid_signature: true}} = conn, opts) do def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
conn conn
end end
def call(conn, opts) do def call(conn, _opts) do
user = conn.params["actor"] user = conn.params["actor"]
Logger.debug("Checking sig for #{user}") Logger.debug("Checking sig for #{user}")
[signature | _] = get_req_header(conn, "signature") [signature | _] = get_req_header(conn, "signature")

View file

@ -1,6 +1,6 @@
defmodule Pleroma.Stats do defmodule Pleroma.Stats do
import Ecto.Query import Ecto.Query
alias Pleroma.{User, Repo, Activity} alias Pleroma.{User, Repo}
def start_link do def start_link do
agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__) agent = Agent.start_link(fn -> {[], %{}} end, name: __MODULE__)

View file

@ -1,6 +1,6 @@
defmodule Pleroma.Web.ActivityPub.ActivityPub do defmodule Pleroma.Web.ActivityPub.ActivityPub do
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification} alias Pleroma.{Activity, Repo, Object, Upload, User, Notification}
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF}
alias Pleroma.Web.WebFinger alias Pleroma.Web.WebFinger
alias Pleroma.Web.Federator alias Pleroma.Web.Federator
alias Pleroma.Web.OStatus alias Pleroma.Web.OStatus
@ -11,7 +11,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
@httpoison Application.get_env(:pleroma, :httpoison) @httpoison Application.get_env(:pleroma, :httpoison)
@instance Application.get_env(:pleroma, :instance) @instance Application.get_env(:pleroma, :instance)
@rewrite_policy Keyword.get(@instance, :rewrite_policy)
def get_recipients(data) do def get_recipients(data) do
(data["to"] || []) ++ (data["cc"] || []) (data["to"] || []) ++ (data["cc"] || [])
@ -20,8 +19,8 @@ def get_recipients(data) do
def insert(map, local \\ true) when is_map(map) do def insert(map, local \\ true) when is_map(map) do
with nil <- Activity.get_by_ap_id(map["id"]), with nil <- Activity.get_by_ap_id(map["id"]),
map <- lazy_put_activity_defaults(map), map <- lazy_put_activity_defaults(map),
:ok <- insert_full_object(map), {:ok, map} <- MRF.filter(map),
{:ok, map} <- @rewrite_policy.filter(map) do :ok <- insert_full_object(map) do
{:ok, activity} = {:ok, activity} =
Repo.insert(%Activity{ Repo.insert(%Activity{
data: map, data: map,
@ -66,7 +65,7 @@ def create(%{to: to, actor: actor, context: context, object: object} = params) d
), ),
{:ok, activity} <- insert(create_data, local), {:ok, activity} <- insert(create_data, local),
:ok <- maybe_federate(activity), :ok <- maybe_federate(activity),
{:ok, actor} <- User.increase_note_count(actor) do {:ok, _actor} <- User.increase_note_count(actor) do
{:ok, activity} {:ok, activity}
end end
end end
@ -177,7 +176,7 @@ def delete(%Object{data: %{"id" => id, "actor" => actor}} = object, local \\ tru
Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)), Repo.delete_all(Activity.all_non_create_by_object_ap_id_q(id)),
{:ok, activity} <- insert(data, local), {:ok, activity} <- insert(data, local),
:ok <- maybe_federate(activity), :ok <- maybe_federate(activity),
{:ok, actor} <- User.decrease_note_count(user) do {:ok, _actor} <- User.decrease_note_count(user) do
{:ok, activity} {:ok, activity}
end end
end end
@ -236,7 +235,7 @@ defp restrict_tag(query, %{"tag" => tag}) do
defp restrict_tag(query, _), do: query defp restrict_tag(query, _), do: query
defp restrict_recipients(query, [], user), do: query defp restrict_recipients(query, [], _user), do: query
defp restrict_recipients(query, recipients, nil) do defp restrict_recipients(query, recipients, nil) do
from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients)) from(activity in query, where: fragment("? && ?", ^recipients, activity.recipients))
@ -313,7 +312,9 @@ defp restrict_recent(query, _) do
defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
blocks = info["blocks"] || [] blocks = info["blocks"] || []
from(activity in query,
from(
activity in query,
where: fragment("not (? = ANY(?))", activity.actor, ^blocks), where: fragment("not (? = ANY(?))", activity.actor, ^blocks),
where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks) where: fragment("not (?->'to' \\?| ?)", activity.data, ^blocks)
) )
@ -405,7 +406,7 @@ def fetch_and_prepare_user_from_ap_id(ap_id) do
end end
def make_user_from_ap_id(ap_id) do def make_user_from_ap_id(ap_id) do
if user = User.get_by_ap_id(ap_id) do if _user = User.get_by_ap_id(ap_id) do
Transmogrifier.upgrade_user_from_ap_id(ap_id) Transmogrifier.upgrade_user_from_ap_id(ap_id)
else else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id) do
@ -501,7 +502,7 @@ def fetch_object_from_id(id) do
object = %Object{} -> object = %Object{} ->
{:ok, object} {:ok, object}
e -> _e ->
Logger.info("Couldn't get object via AP, trying out OStatus fetching...") Logger.info("Couldn't get object via AP, trying out OStatus fetching...")
case OStatus.fetch_activity_from_url(id) do case OStatus.fetch_activity_from_url(id) do

View file

@ -1,7 +1,7 @@
defmodule Pleroma.Web.ActivityPub.ActivityPubController do defmodule Pleroma.Web.ActivityPub.ActivityPubController do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.{User, Repo, Object, Activity} alias Pleroma.{User, Object}
alias Pleroma.Web.ActivityPub.{ObjectView, UserView, Transmogrifier} alias Pleroma.Web.ActivityPub.{ObjectView, UserView}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.Federator alias Pleroma.Web.Federator

View file

@ -0,0 +1,24 @@
defmodule Pleroma.Web.ActivityPub.MRF do
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
def filter(object) do
get_policies()
|> Enum.reduce({:ok, object}, fn
policy, {:ok, object} ->
policy.filter(object)
_, error ->
error
end)
end
def get_policies() do
Application.get_env(:pleroma, :instance, [])
|> Keyword.get(:rewrite_policy, [])
|> get_policies()
end
defp get_policies(policy) when is_atom(policy), do: [policy]
defp get_policies(policies) when is_list(policies), do: policies
defp get_policies(_), do: []
end

View file

@ -1,6 +1,8 @@
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
require Logger require Logger
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do def filter(object) do
Logger.info("REJECTING #{inspect(object)}") Logger.info("REJECTING #{inspect(object)}")
{:reject, object} {:reject, object}

View file

@ -1,4 +1,7 @@
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(object) do def filter(object) do
{:ok, object} {:ok, object}
end end

View file

@ -1,5 +1,6 @@
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
alias Pleroma.User alias Pleroma.User
@behaviour Pleroma.Web.ActivityPub.MRF
@mrf_policy Application.get_env(:pleroma, :mrf_simple) @mrf_policy Application.get_env(:pleroma, :mrf_simple)
@ -17,9 +18,10 @@ defp check_media_removal(actor_info, object) do
if actor_info.host in @media_removal do if actor_info.host in @media_removal do
child_object = Map.delete(object["object"], "attachment") child_object = Map.delete(object["object"], "attachment")
object = Map.put(object, "object", child_object) object = Map.put(object, "object", child_object)
end
{:ok, object} {:ok, object}
else
{:ok, object}
end
end end
@media_nsfw Keyword.get(@mrf_policy, :media_nsfw) @media_nsfw Keyword.get(@mrf_policy, :media_nsfw)
@ -32,9 +34,10 @@ defp check_media_nsfw(actor_info, object) do
child_object = Map.put(child_object, "tags", tags) child_object = Map.put(child_object, "tags", tags)
child_object = Map.put(child_object, "sensitive", true) child_object = Map.put(child_object, "sensitive", true)
object = Map.put(object, "object", child_object) object = Map.put(object, "object", child_object)
end
{:ok, object} {:ok, object}
else
{:ok, object}
end
end end
@ftl_removal Keyword.get(@mrf_policy, :federated_timeline_removal) @ftl_removal Keyword.get(@mrf_policy, :federated_timeline_removal)
@ -43,6 +46,7 @@ defp check_ftl_removal(actor_info, object) do
user = User.get_by_ap_id(object["actor"]) user = User.get_by_ap_id(object["actor"])
# flip to/cc relationship to make the post unlisted # flip to/cc relationship to make the post unlisted
object =
if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and if "https://www.w3.org/ns/activitystreams#Public" in object["to"] and
user.follower_address in object["cc"] do user.follower_address in object["cc"] do
to = to =
@ -53,14 +57,20 @@ defp check_ftl_removal(actor_info, object) do
List.delete(object["cc"], user.follower_address) ++ List.delete(object["cc"], user.follower_address) ++
["https://www.w3.org/ns/activitystreams#Public"] ["https://www.w3.org/ns/activitystreams#Public"]
object = Map.put(object, "to", to) object
object = Map.put(object, "cc", cc) |> Map.put("to", to)
end |> Map.put("cc", cc)
else
object
end end
{:ok, object} {:ok, object}
else
{:ok, object}
end
end end
@impl true
def filter(object) do def filter(object) do
actor_info = URI.parse(object["actor"]) actor_info = URI.parse(object["actor"])
@ -70,7 +80,7 @@ def filter(object) do
{:ok, object} <- check_ftl_removal(actor_info, object) do {:ok, object} <- check_ftl_removal(actor_info, object) do
{:ok, object} {:ok, object}
else else
e -> {:reject, nil} _e -> {:reject, nil}
end end
end end
end end

View file

@ -72,8 +72,11 @@ def fix_emoji(object) do
|> Enum.reduce(%{}, fn data, mapping -> |> Enum.reduce(%{}, fn data, mapping ->
name = data["name"] name = data["name"]
name =
if String.starts_with?(name, ":") do if String.starts_with?(name, ":") do
name = name |> String.slice(1..-2) name |> String.slice(1..-2)
else
name
end end
mapping |> Map.put(name, data["icon"]["url"]) mapping |> Map.put(name, data["icon"]["url"])
@ -143,12 +146,12 @@ def handle_incoming(
end end
def handle_incoming( def handle_incoming(
%{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = data %{"type" => "Like", "object" => object_id, "actor" => actor, "id" => id} = _data
) do ) do
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- {:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity, object} <- ActivityPub.like(actor, object, id, false) do {:ok, activity, _object} <- ActivityPub.like(actor, object, id, false) do
{:ok, activity} {:ok, activity}
else else
_e -> :error _e -> :error
@ -156,12 +159,12 @@ def handle_incoming(
end end
def handle_incoming( def handle_incoming(
%{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = data %{"type" => "Announce", "object" => object_id, "actor" => actor, "id" => id} = _data
) do ) do
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- {:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity, object} <- ActivityPub.announce(actor, object, id, false) do {:ok, activity, _object} <- ActivityPub.announce(actor, object, id, false) do
{:ok, activity} {:ok, activity}
else else
_e -> :error _e -> :error
@ -202,7 +205,7 @@ def handle_incoming(
# TODO: Make secure. # TODO: Make secure.
def handle_incoming( def handle_incoming(
%{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => id} = data %{"type" => "Delete", "object" => object_id, "actor" => actor, "id" => _id} = _data
) do ) do
object_id = object_id =
case object_id do case object_id do
@ -210,13 +213,13 @@ def handle_incoming(
id -> id id -> id
end end
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor), with %User{} = _actor <- User.get_or_fetch_by_ap_id(actor),
{:ok, object} <- {:ok, object} <-
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id), get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
{:ok, activity} <- ActivityPub.delete(object, false) do {:ok, activity} <- ActivityPub.delete(object, false) do
{:ok, activity} {:ok, activity}
else else
e -> :error _e -> :error
end end
end end
@ -254,10 +257,10 @@ def prepare_object(object) do
|> set_reply_to_uri |> set_reply_to_uri
end end
@doc # @doc
""" # """
internal -> Mastodon # internal -> Mastodon
""" # """
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do
object = object =
@ -272,7 +275,7 @@ def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = obj
{:ok, data} {:ok, data}
end end
def prepare_outgoing(%{"type" => type} = data) do def prepare_outgoing(%{"type" => _type} = data) do
data = data =
data data
|> maybe_fix_object_url |> maybe_fix_object_url
@ -286,7 +289,7 @@ def maybe_fix_object_url(data) do
case ActivityPub.fetch_object_from_id(data["object"]) do case ActivityPub.fetch_object_from_id(data["object"]) do
{:ok, relative_object} -> {:ok, relative_object} ->
if relative_object.data["external_url"] do if relative_object.data["external_url"] do
data = _data =
data data
|> Map.put("object", relative_object.data["external_url"]) |> Map.put("object", relative_object.data["external_url"])
else else

View file

@ -47,25 +47,6 @@ def render("user.json", %{user: user}) do
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
def collection(collection, iri, page, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{
"id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => length(collection),
"orderedItems" => items
}
if offset < length(collection) do
Map.put(map, "next", "#{iri}?page=#{page + 1}")
end
end
def render("following.json", %{user: user, page: page}) do def render("following.json", %{user: user, page: page}) do
query = User.get_friends_query(user) query = User.get_friends_query(user)
query = from(user in query, select: [:ap_id]) query = from(user in query, select: [:ap_id])
@ -123,8 +104,11 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
"limit" => "10" "limit" => "10"
} }
params =
if max_qid != nil do if max_qid != nil do
params = Map.put(params, "max_id", max_qid) Map.put(params, "max_id", max_qid)
else
params
end end
activities = ActivityPub.fetch_public_activities(params) activities = ActivityPub.fetch_public_activities(params)
@ -162,4 +146,23 @@ def render("outbox.json", %{user: user, max_id: max_qid}) do
page |> Map.merge(Utils.make_json_ld_header()) page |> Map.merge(Utils.make_json_ld_header())
end end
end end
def collection(collection, iri, page, total \\ nil) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn user -> user.ap_id end)
total = total || length(collection)
map = %{
"id" => "#{iri}?page=#{page}",
"type" => "OrderedCollectionPage",
"partOf" => iri,
"totalItems" => total,
"orderedItems" => items
}
if offset < total do
Map.put(map, "next", "#{iri}?page=#{page + 1}")
end
end
end end

View file

@ -1,7 +1,6 @@
defmodule Pleroma.Web.UserSocket do defmodule Pleroma.Web.UserSocket do
use Phoenix.Socket use Phoenix.Socket
alias Pleroma.User alias Pleroma.User
alias Comeonin.Pbkdf2
## Channels ## Channels
# channel "room:*", Pleroma.Web.RoomChannel # channel "room:*", Pleroma.Web.RoomChannel

View file

@ -1,5 +1,5 @@
defmodule Pleroma.Web.CommonAPI do defmodule Pleroma.Web.CommonAPI do
alias Pleroma.{Repo, Activity, Object, User} alias Pleroma.{Repo, Activity, Object}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Formatter alias Pleroma.Formatter

View file

@ -1,5 +1,5 @@
defmodule Pleroma.Web.CommonAPI.Utils do defmodule Pleroma.Web.CommonAPI.Utils do
alias Pleroma.{Repo, Object, Formatter, User, Activity} alias Pleroma.{Repo, Object, Formatter, Activity}
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Calendar.Strftime alias Calendar.Strftime
@ -49,7 +49,7 @@ def to_for_user_and_mentions(user, mentions, inReplyTo, "private") do
{[user.follower_address | to], cc} {[user.follower_address | to], cc}
end end
def to_for_user_and_mentions(user, mentions, inReplyTo, "direct") do def to_for_user_and_mentions(_user, mentions, inReplyTo, "direct") do
mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end) mentioned_users = Enum.map(mentions, fn {_, %{ap_id: ap_id}} -> ap_id end)
if inReplyTo do if inReplyTo do
@ -69,7 +69,7 @@ def make_content_html(status, mentions, attachments, tags, no_attachment_links \
def make_context(%Activity{data: %{"context" => context}}), do: context def make_context(%Activity{data: %{"context" => context}}), do: context
def make_context(_), do: Utils.generate_context_id() def make_context(_), do: Utils.generate_context_id()
def maybe_add_attachments(text, attachments, _no_links = true), do: text def maybe_add_attachments(text, _attachments, _no_links = true), do: text
def maybe_add_attachments(text, attachments, _no_links) do def maybe_add_attachments(text, attachments, _no_links) do
add_attachments(text, attachments) add_attachments(text, attachments)

View file

@ -14,6 +14,10 @@ defmodule Pleroma.Web.Federator do
@federating Keyword.get(@instance, :federating) @federating Keyword.get(@instance, :federating)
@max_jobs 20 @max_jobs 20
def init(args) do
{:ok, args}
end
def start_link do def start_link do
spawn(fn -> spawn(fn ->
# 1 minute # 1 minute
@ -89,12 +93,12 @@ def handle(:incoming_ap_doc, params) do
with {:ok, _user} <- ap_enabled_actor(params["actor"]), with {:ok, _user} <- ap_enabled_actor(params["actor"]),
nil <- Activity.get_by_ap_id(params["id"]), nil <- Activity.get_by_ap_id(params["id"]),
{:ok, activity} <- Transmogrifier.handle_incoming(params) do {:ok, _activity} <- Transmogrifier.handle_incoming(params) do
else else
%Activity{} -> %Activity{} ->
Logger.info("Already had #{params["id"]}") Logger.info("Already had #{params["id"]}")
e -> _e ->
# Just drop those for now # Just drop those for now
Logger.info("Unhandled activity") Logger.info("Unhandled activity")
Logger.info(Poison.encode!(params, pretty: 2)) Logger.info(Poison.encode!(params, pretty: 2))
@ -154,7 +158,7 @@ def maybe_start_job(running_jobs, queue) do
end end
end end
def handle_cast({:enqueue, type, payload, priority}, state) def handle_cast({:enqueue, type, payload, _priority}, state)
when type in [:incoming_doc, :incoming_ap_doc] do when type in [:incoming_doc, :incoming_ap_doc] do
%{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
i_queue = enqueue_sorted(i_queue, {type, payload}, 1) i_queue = enqueue_sorted(i_queue, {type, payload}, 1)
@ -162,7 +166,7 @@ def handle_cast({:enqueue, type, payload, priority}, state)
{:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}} {:noreply, %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}}}
end end
def handle_cast({:enqueue, type, payload, priority}, state) do def handle_cast({:enqueue, type, payload, _priority}, state) do
%{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state %{in: {i_running_jobs, i_queue}, out: {o_running_jobs, o_queue}} = state
o_queue = enqueue_sorted(o_queue, {type, payload}, 1) o_queue = enqueue_sorted(o_queue, {type, payload}, 1)
{o_running_jobs, o_queue} = maybe_start_job(o_running_jobs, o_queue) {o_running_jobs, o_queue} = maybe_start_job(o_running_jobs, o_queue)

View file

@ -45,7 +45,7 @@ def validate_conn(conn) do
end end
end end
else else
e -> _e ->
Logger.debug("Could not public key!") Logger.debug("Could not public key!")
false false
end end

View file

@ -112,7 +112,7 @@ def masto_instance(conn, _params) do
version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})", version: "#{@mastodon_api_level} (compatible; #{Keyword.get(@instance, :version)})",
email: Keyword.get(@instance, :email), email: Keyword.get(@instance, :email),
urls: %{ urls: %{
streaming_api: String.replace(Web.base_url(), ["http", "https"], "wss") streaming_api: String.replace(Pleroma.Web.Endpoint.static_url(), "http", "ws")
}, },
stats: Stats.get_stats(), stats: Stats.get_stats(),
thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg", thumbnail: Web.base_url() <> "/instance/thumbnail.jpeg",
@ -212,11 +212,11 @@ def user_statuses(%{assigns: %{user: user}} = conn, params) do
|> Map.put("actor_id", ap_id) |> Map.put("actor_id", ap_id)
|> Map.put("whole_db", true) |> Map.put("whole_db", true)
activities =
if params["pinned"] == "true" do if params["pinned"] == "true" do
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here # Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
activities = [] []
else else
activities =
ActivityPub.fetch_public_activities(params) ActivityPub.fetch_public_activities(params)
|> Enum.reverse() |> Enum.reverse()
end end
@ -275,7 +275,19 @@ def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
|> Map.put("in_reply_to_status_id", params["in_reply_to_id"]) |> Map.put("in_reply_to_status_id", params["in_reply_to_id"])
|> Map.put("no_attachment_links", true) |> Map.put("no_attachment_links", true)
{:ok, activity} = CommonAPI.post(user, params) idempotency_key =
case get_req_header(conn, "idempotency-key") do
[key] -> key
_ -> Ecto.UUID.generate()
end
{:ok, activity} =
Cachex.get!(
:idempotency_cache,
idempotency_key,
fallback: fn _ -> CommonAPI.post(user, params) end
)
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity}) render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
end end

View file

@ -82,19 +82,6 @@ def render(
} }
end end
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
id = activity.data["object"]["inReplyTo"]
replied_to_activities[activity.data["object"]["inReplyTo"]]
end
def get_reply_to(%{data: %{"object" => object}}, _) do
if object["inReplyTo"] && object["inReplyTo"] != "" do
Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
else
nil
end
end
def render("status.json", %{activity: %{data: %{"object" => object}} = activity} = opts) do def render("status.json", %{activity: %{data: %{"object" => object}} = activity} = opts) do
user = User.get_cached_by_ap_id(activity.data["actor"]) user = User.get_cached_by_ap_id(activity.data["actor"])
@ -164,19 +151,6 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
} }
end end
def get_visibility(object) do
public = "https://www.w3.org/ns/activitystreams#Public"
to = object["to"] || []
cc = object["cc"] || []
cond do
public in to -> "public"
public in cc -> "unlisted"
Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
true -> "direct"
end
end
def render("attachment.json", %{attachment: attachment}) do def render("attachment.json", %{attachment: attachment}) do
[%{"mediaType" => media_type, "href" => href} | _] = attachment["url"] [%{"mediaType" => media_type, "href" => href} | _] = attachment["url"]
@ -199,4 +173,30 @@ def render("attachment.json", %{attachment: attachment}) do
type: type type: type
} }
end end
def get_reply_to(activity, %{replied_to_activities: replied_to_activities}) do
_id = activity.data["object"]["inReplyTo"]
replied_to_activities[activity.data["object"]["inReplyTo"]]
end
def get_reply_to(%{data: %{"object" => object}}, _) do
if object["inReplyTo"] && object["inReplyTo"] != "" do
Activity.get_create_activity_by_object_ap_id(object["inReplyTo"])
else
nil
end
end
def get_visibility(object) do
public = "https://www.w3.org/ns/activitystreams#Public"
to = object["to"] || []
cc = object["cc"] || []
cond do
public in to -> "public"
public in cc -> "unlisted"
Enum.any?(to, &String.contains?(&1, "/followers")) -> "private"
true -> "direct"
end
end
end end

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,62 @@
defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
use Pleroma.Web, :controller
alias Pleroma.Stats
alias Pleroma.Web
@instance Application.get_env(:pleroma, :instance)
def schemas(conn, _params) do
response = %{
links: [
%{
rel: "http://nodeinfo.diaspora.software/ns/schema/2.0",
href: Web.base_url() <> "/nodeinfo/2.0.json"
}
]
}
json(conn, response)
end
# Schema definition: https://github.com/jhass/nodeinfo/blob/master/schemas/2.0/schema.json
def nodeinfo(conn, %{"version" => "2.0"}) do
stats = Stats.get_stats()
response = %{
version: "2.0",
software: %{
name: "pleroma",
version: Keyword.get(@instance, :version)
},
protocols: ["ostatus", "activitypub"],
services: %{
inbound: [],
outbound: []
},
openRegistrations: Keyword.get(@instance, :registrations_open),
usage: %{
users: %{
total: stats.user_count || 0
},
localPosts: stats.status_count || 0
},
metadata: %{
nodeName: Keyword.get(@instance, :name)
}
}
conn
|> put_resp_header(
"content-type",
"application/json; profile=http://nodeinfo.diaspora.software/ns/schema/2.0#; charset=utf-8"
)
|> json(response)
end
def nodeinfo(conn, _) do
conn
|> put_status(404)
|> json(%{error: "Nodeinfo schema version not handled"})
end
end

View file

@ -11,7 +11,7 @@ defmodule Pleroma.Web.OAuth.Authorization do
field(:valid_until, :naive_datetime) field(:valid_until, :naive_datetime)
field(:used, :boolean, default: false) field(:used, :boolean, default: false)
belongs_to(:user, Pleroma.User) belongs_to(:user, Pleroma.User)
belongs_to(:app, Pleroma.App) belongs_to(:app, App)
timestamps() timestamps()
end end

View file

@ -9,7 +9,7 @@ defmodule Pleroma.Web.OAuth.Token do
field(:refresh_token, :string) field(:refresh_token, :string)
field(:valid_until, :naive_datetime) field(:valid_until, :naive_datetime)
belongs_to(:user, Pleroma.User) belongs_to(:user, Pleroma.User)
belongs_to(:app, Pleroma.App) belongs_to(:app, App)
timestamps() timestamps()
end end

View file

@ -1,7 +1,6 @@
defmodule Pleroma.Web.OStatus.ActivityRepresenter do defmodule Pleroma.Web.OStatus.ActivityRepresenter do
alias Pleroma.{Activity, User, Object} alias Pleroma.{Activity, User, Object}
alias Pleroma.Web.OStatus.UserRepresenter alias Pleroma.Web.OStatus.UserRepresenter
alias Pleroma.Formatter
require Logger require Logger
defp get_href(id) do defp get_href(id) do

View file

@ -1,7 +1,7 @@
defmodule Pleroma.Web.OStatus.NoteHandler do defmodule Pleroma.Web.OStatus.NoteHandler do
require Logger require Logger
alias Pleroma.Web.{XML, OStatus} alias Pleroma.Web.{XML, OStatus}
alias Pleroma.{Object, User, Activity} alias Pleroma.{Object, Activity}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI

View file

@ -8,7 +8,6 @@ defmodule Pleroma.Web.OStatus.OStatusController do
alias Pleroma.Web.XML alias Pleroma.Web.XML
alias Pleroma.Web.ActivityPub.ActivityPubController alias Pleroma.Web.ActivityPub.ActivityPubController
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
import Ecto.Query
def feed_redirect(conn, %{"nickname" => nickname} = params) do def feed_redirect(conn, %{"nickname" => nickname} = params) do
user = User.get_cached_by_nickname(nickname) user = User.get_cached_by_nickname(nickname)

View file

@ -295,6 +295,11 @@ def user_fetcher(username) do
get("/host-meta", WebFinger.WebFingerController, :host_meta) get("/host-meta", WebFinger.WebFingerController, :host_meta)
get("/webfinger", WebFinger.WebFingerController, :webfinger) get("/webfinger", WebFinger.WebFingerController, :webfinger)
get("/nodeinfo", Nodeinfo.NodeinfoController, :schemas)
end
scope "/nodeinfo", Pleroma.Web do
get("/:version", Nodeinfo.NodeinfoController, :nodeinfo)
end end
end end

View file

@ -3,6 +3,10 @@ defmodule Pleroma.Web.Streamer do
require Logger require Logger
alias Pleroma.{User, Notification} alias Pleroma.{User, Notification}
def init(args) do
{:ok, args}
end
def start_link do def start_link do
spawn(fn -> spawn(fn ->
# 30 seconds # 30 seconds
@ -110,6 +114,11 @@ def handle_cast(m, state) do
def push_to_socket(topics, topic, item) do def push_to_socket(topics, topic, item) do
Enum.each(topics[topic] || [], fn socket -> Enum.each(topics[topic] || [], fn socket ->
# Get the current user so we have up-to-date blocks etc.
user = User.get_cached_by_ap_id(socket.assigns[:user].ap_id)
blocks = user.info["blocks"] || []
unless item.actor in blocks do
json = json =
%{ %{
event: "update", event: "update",
@ -117,13 +126,14 @@ def push_to_socket(topics, topic, item) do
Pleroma.Web.MastodonAPI.StatusView.render( Pleroma.Web.MastodonAPI.StatusView.render(
"status.json", "status.json",
activity: item, activity: item,
for: socket.assigns[:user] for: user
) )
|> Jason.encode!() |> Jason.encode!()
} }
|> Jason.encode!() |> Jason.encode!()
send(socket.transport_pid, {:text, json}) send(socket.transport_pid, {:text, json})
end
end) end)
end end

View file

@ -92,7 +92,7 @@ def do_remote_follow(conn, %{
with %User{} = user <- User.get_cached_by_nickname(username), with %User{} = user <- User.get_cached_by_nickname(username),
true <- Pbkdf2.checkpw(password, user.password_hash), true <- Pbkdf2.checkpw(password, user.password_hash),
%User{} = followed <- Repo.get(User, id), %User{} = _followed <- Repo.get(User, id),
{:ok, follower} <- User.follow(user, followee), {:ok, follower} <- User.follow(user, followee),
{:ok, _activity} <- ActivityPub.follow(follower, followee) do {:ok, _activity} <- ActivityPub.follow(follower, followee) do
conn conn

View file

@ -1,7 +1,6 @@
defmodule Pleroma.Web.TwitterAPI.TwitterAPI do defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
alias Pleroma.{User, Activity, Repo, Object} alias Pleroma.{User, Activity, Repo, Object}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
alias Pleroma.Web.TwitterAPI.UserView alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.{OStatus, CommonAPI} alias Pleroma.Web.{OStatus, CommonAPI}
import Ecto.Query import Ecto.Query
@ -184,7 +183,7 @@ defp parse_int(string, default) when is_binary(string) do
defp parse_int(_, default), do: default defp parse_int(_, default), do: default
def search(user, %{"q" => query} = params) do def search(_user, %{"q" => query} = params) do
limit = parse_int(params["rpp"], 20) limit = parse_int(params["rpp"], 20)
page = parse_int(params["page"], 1) page = parse_int(params["page"], 1)
offset = (page - 1) * limit offset = (page - 1) * limit
@ -206,7 +205,7 @@ def search(user, %{"q" => query} = params) do
order_by: [desc: :inserted_at] order_by: [desc: :inserted_at]
) )
activities = Repo.all(q) _activities = Repo.all(q)
end end
defp make_date do defp make_date do

View file

@ -347,7 +347,8 @@ def empty_array(conn, _params) do
def update_profile(%{assigns: %{user: user}} = conn, params) do def update_profile(%{assigns: %{user: user}} = conn, params) do
params = params =
if bio = params["description"] do if bio = params["description"] do
Map.put(params, "bio", bio) bio_brs = Regex.replace(~r/\r?\n/, bio, "<br>")
Map.put(params, "bio", bio_brs)
else else
params params
end end

View file

@ -31,7 +31,7 @@ defp query_users(user_ids) do
end end
defp collect_context_ids(activities) do defp collect_context_ids(activities) do
contexts = _contexts =
activities activities
|> Enum.reject(& &1.data["context_id"]) |> Enum.reject(& &1.data["context_id"])
|> Enum.map(fn %{data: data} -> |> Enum.map(fn %{data: data} ->

View file

@ -2,7 +2,6 @@ defmodule Pleroma.Web.TwitterAPI.NotificationView do
use Pleroma.Web, :view use Pleroma.Web, :view
alias Pleroma.{Notification, User} alias Pleroma.{Notification, User}
alias Pleroma.Web.CommonAPI.Utils alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MediaProxy
alias Pleroma.Web.TwitterAPI.UserView alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.TwitterAPI.ActivityView alias Pleroma.Web.TwitterAPI.ActivityView

View file

@ -1,7 +1,7 @@
defmodule Pleroma.Web.WebFinger do defmodule Pleroma.Web.WebFinger do
@httpoison Application.get_env(:pleroma, :httpoison) @httpoison Application.get_env(:pleroma, :httpoison)
alias Pleroma.{Repo, User, XmlBuilder} alias Pleroma.{User, XmlBuilder}
alias Pleroma.Web alias Pleroma.Web
alias Pleroma.Web.{XML, Salmon, OStatus} alias Pleroma.Web.{XML, Salmon, OStatus}
require Jason require Jason
@ -239,12 +239,13 @@ def finger(account) do
URI.parse(account).host URI.parse(account).host
end end
address =
case find_lrdd_template(domain) do case find_lrdd_template(domain) do
{:ok, template} -> {:ok, template} ->
address = String.replace(template, "{uri}", URI.encode(account)) String.replace(template, "{uri}", URI.encode(account))
_ -> _ ->
address = "http://#{domain}/.well-known/webfinger?resource=acct:#{account}" "http://#{domain}/.well-known/webfinger?resource=acct:#{account}"
end end
with response <- with response <-

View file

@ -14,7 +14,7 @@ def string_from_xpath(xpath, doc) do
if res == "", do: nil, else: res if res == "", do: nil, else: res
catch catch
e -> _e ->
Logger.debug("Couldn't find xpath #{xpath} in XML doc") Logger.debug("Couldn't find xpath #{xpath} in XML doc")
nil nil
end end

View file

@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.6c8da1b0ace79ad8881a0e5e716ec818.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.cdeff2a56af285544b89.js></script><script type=text/javascript src=/static/js/vendor.ef2aee0b2db579c3a86a.js></script><script type=text/javascript src=/static/js/app.408cea515c3032097a51.js></script></body></html> <!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.6c8da1b0ace79ad8881a0e5e716ec818.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.38e369a50eccc2857845.js></script><script type=text/javascript src=/static/js/vendor.ef2aee0b2db579c3a86a.js></script><script type=text/javascript src=/static/js/app.af121efa5ff89725b4c6.js></script></body></html>

View file

@ -2,7 +2,8 @@
"theme": "pleroma-dark", "theme": "pleroma-dark",
"background": "/static/aurora_borealis.jpg", "background": "/static/aurora_borealis.jpg",
"logo": "/static/logo.png", "logo": "/static/logo.png",
"defaultPath": "/main/all", "redirectRootNoLogin": "/main/all",
"redirectRootLogin": "/main/friends",
"chatDisabled": false, "chatDisabled": false,
"showInstanceSpecificPanel": true "showInstanceSpecificPanel": false
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,2 @@
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(o,c){for(var p,l,s=0,f=[];s<o.length;s++)l=o[s],n[l]&&f.push.apply(f,n[l]),n[l]=0;for(p in c)Object.prototype.hasOwnProperty.call(c,p)&&(e[p]=c[p]);for(a&&a(o,c);f.length;)f.shift().call(null,t);if(c[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],o=document.createElement("script");o.type="text/javascript",o.charset="utf-8",o.async=!0,o.src=t.p+"static/js/"+e+"."+{1:"ef2aee0b2db579c3a86a",2:"af121efa5ff89725b4c6"}[e]+".js",r.appendChild(o)}},t.m=e,t.c=r,t.p="/"}([]);
//# sourceMappingURL=manifest.38e369a50eccc2857845.js.map

View file

@ -1,2 +0,0 @@
!function(e){function t(a){if(r[a])return r[a].exports;var n=r[a]={exports:{},id:a,loaded:!1};return e[a].call(n.exports,n,n.exports,t),n.loaded=!0,n.exports}var a=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,l,s=0,i=[];s<c.length;s++)l=c[s],n[l]&&i.push.apply(i,n[l]),n[l]=0;for(p in o)Object.prototype.hasOwnProperty.call(o,p)&&(e[p]=o[p]);for(a&&a(c,o);i.length;)i.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},n={0:0};t.e=function(e,a){if(0===n[e])return a.call(null,t);if(void 0!==n[e])n[e].push(a);else{n[e]=[a];var r=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.src=t.p+"static/js/"+e+"."+{1:"ef2aee0b2db579c3a86a",2:"408cea515c3032097a51"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
//# sourceMappingURL=manifest.cdeff2a56af285544b89.js.map

View file

@ -33,6 +33,13 @@ test "it doesn't create a notification for user if the user blocks the activity
assert nil == Notification.create_notification(activity, user) assert nil == Notification.create_notification(activity, user)
end end
test "it doesn't create a notification for user if he is the activity author" do
activity = insert(:note_activity)
author = User.get_by_ap_id(activity.data["actor"])
assert nil == Notification.create_notification(activity, author)
end
end end
describe "get notification" do describe "get notification" do

View file

@ -26,7 +26,7 @@ def insert(data \\ %{}, opts \\ %{}) do
end end
def insert_list(times, data \\ %{}, opts \\ %{}) do def insert_list(times, data \\ %{}, opts \\ %{}) do
Enum.map(1..times, fn n -> Enum.map(1..times, fn _n ->
{:ok, activity} = insert(data, opts) {:ok, activity} = insert(data, opts)
activity activity
end) end)

View file

@ -367,7 +367,7 @@ def get("https://shitposter.club/api/statuses/user_timeline/1.atom", _body, _hea
def post( def post(
"https://social.heldscal.la/main/push/hub", "https://social.heldscal.la/main/push/hub",
{:form, data}, {:form, _data},
"Content-type": "application/x-www-form-urlencoded" "Content-type": "application/x-www-form-urlencoded"
) do ) do
{:ok, {:ok,
@ -711,11 +711,11 @@ def get(url, body, headers) do
}"} }"}
end end
def post(url, body, headers) do def post(url, _body, _headers) do
{:error, "Not implemented the mock response for post #{inspect(url)}"} {:error, "Not implemented the mock response for post #{inspect(url)}"}
end end
def post(url, body, headers, options) do def post(url, _body, _headers, _options) do
{:error, "Not implemented the mock response for post #{inspect(url)}"} {:error, "Not implemented the mock response for post #{inspect(url)}"}
end end
end end

View file

@ -63,7 +63,42 @@ test "the public timeline", %{conn: conn} do
test "posting a status", %{conn: conn} do test "posting a status", %{conn: conn} do
user = insert(:user) user = insert(:user)
conn = idempotency_key = "Pikachu rocks!"
conn_one =
conn
|> assign(:user, user)
|> put_req_header("idempotency-key", idempotency_key)
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => "false"
})
{:ok, ttl} = Cachex.ttl(:idempotency_cache, idempotency_key)
# Six hours
assert ttl > :timer.seconds(6 * 60 * 60 - 1)
assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} =
json_response(conn_one, 200)
assert Repo.get(Activity, id)
conn_two =
conn
|> assign(:user, user)
|> put_req_header("idempotency-key", idempotency_key)
|> post("/api/v1/statuses", %{
"status" => "cofe",
"spoiler_text" => "2hu",
"sensitive" => "false"
})
assert %{"id" => second_id} = json_response(conn_two, 200)
assert id == second_id
conn_three =
conn conn
|> assign(:user, user) |> assign(:user, user)
|> post("/api/v1/statuses", %{ |> post("/api/v1/statuses", %{
@ -72,10 +107,9 @@ test "posting a status", %{conn: conn} do
"sensitive" => "false" "sensitive" => "false"
}) })
assert %{"content" => "cofe", "id" => id, "spoiler_text" => "2hu", "sensitive" => false} = assert %{"id" => third_id} = json_response(conn_three, 200)
json_response(conn, 200)
assert Repo.get(Activity, id) refute id == third_id
end end
test "posting a sensitive status", %{conn: conn} do test "posting a sensitive status", %{conn: conn} do

View file

@ -0,0 +1,63 @@
defmodule Pleroma.Web.StreamerTest do
use Pleroma.DataCase
alias Pleroma.Web.Streamer
alias Pleroma.User
alias Pleroma.Web.CommonAPI
import Pleroma.Factory
test "it sends to public" do
user = insert(:user)
other_user = insert(:user)
task =
Task.async(fn ->
assert_receive {:text, _}, 4_000
end)
fake_socket = %{
transport_pid: task.pid,
assigns: %{
user: user
}
}
{:ok, activity} = CommonAPI.post(other_user, %{"status" => "Test"})
topics = %{
"public" => [fake_socket]
}
Streamer.push_to_socket(topics, "public", activity)
Task.await(task)
end
test "it doesn't send to blocked users" do
user = insert(:user)
blocked_user = insert(:user)
{:ok, user} = User.block(user, blocked_user)
task =
Task.async(fn ->
refute_receive {:text, _}, 1_000
end)
fake_socket = %{
transport_pid: task.pid,
assigns: %{
user: user
}
}
{:ok, activity} = CommonAPI.post(blocked_user, %{"status" => "Test"})
topics = %{
"public" => [fake_socket]
}
Streamer.push_to_socket(topics, "public", activity)
Task.await(task)
end
end

View file

@ -257,8 +257,10 @@ test "without valid credentials", %{conn: conn} do
end end
test "with credentials", %{conn: conn, user: current_user} do test "with credentials", %{conn: conn, user: current_user} do
other_user = insert(:user)
{:ok, activity} = {:ok, activity} =
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user}) ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
conn = conn =
conn conn
@ -784,4 +786,18 @@ test "it returns the tags timeline", %{conn: conn} do
assert status["id"] == activity.id assert status["id"] == activity.id
end end
end end
test "Convert newlines to <br> 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,<br>World! I<br> am a test."
end
end end