Fix merge conflict

This commit is contained in:
rinpatch 2019-01-18 18:31:25 +03:00
commit 74346a7035
39 changed files with 559 additions and 81 deletions

View file

@ -139,7 +139,8 @@
finmoji_enabled: true, finmoji_enabled: true,
mrf_transparency: true, mrf_transparency: true,
autofollowed_nicknames: [], autofollowed_nicknames: [],
max_pinned_statuses: 1 max_pinned_statuses: 1,
no_attachment_links: false
config :pleroma, :markup, config :pleroma, :markup,
# XXX - unfortunately, inline images must be enabled by default right now, because # XXX - unfortunately, inline images must be enabled by default right now, because

View file

@ -95,6 +95,7 @@ config :pleroma, Pleroma.Mailer,
older software for theses nicknames. older software for theses nicknames.
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature. * `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow. * `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses
## :logger ## :logger
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog * `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog

View file

@ -79,8 +79,10 @@ server {
proxy_cache_valid 200 206 301 304 1h; proxy_cache_valid 200 206 301 304 1h;
proxy_cache_lock on; proxy_cache_lock on;
proxy_ignore_client_abort on; proxy_ignore_client_abort on;
proxy_buffering off; proxy_buffering on;
chunked_transfer_encoding on; chunked_transfer_encoding on;
proxy_ignore_headers Cache-Control;
proxy_hide_header Cache-Control;
proxy_pass http://localhost:4000; proxy_pass http://localhost:4000;
} }
} }

View file

@ -3,15 +3,23 @@ Description=Pleroma social network
After=network.target postgresql.service After=network.target postgresql.service
[Service] [Service]
User=pleroma
WorkingDirectory=/home/pleroma/pleroma
Environment="HOME=/home/pleroma"
Environment="MIX_ENV=prod"
ExecStart=/usr/local/bin/mix phx.server
ExecReload=/bin/kill $MAINPID ExecReload=/bin/kill $MAINPID
KillMode=process KillMode=process
Restart=on-failure Restart=on-failure
; Name of the user that runs the Pleroma service.
User=pleroma
; Declares that Pleroma runs in production mode.
Environment="MIX_ENV=prod"
; Make sure that all paths fit your installation.
; Path to the home directory of the user running the Pleroma service.
Environment="HOME=/home/pleroma"
; Path to the folder containing the Pleroma installation.
WorkingDirectory=/home/pleroma/pleroma
; Path to the Mix binary.
ExecStart=/usr/bin/mix phx.server
; Some security directives. ; Some security directives.
; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops. ; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
PrivateTmp=true PrivateTmp=true

View file

@ -155,7 +155,9 @@ def add_user_links({subs, text}, mentions) do
short_match = String.split(match, "@") |> tl() |> hd() short_match = String.split(match, "@") |> tl() |> hd()
{uuid, {uuid,
"<span><a data-user='#{id}' class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"} "<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{
short_match
}</span></a></span>"}
end) end)
{subs, uuid_text} {subs, uuid_text}
@ -178,7 +180,7 @@ def add_hashtag_links({subs, text}, tags) do
subs ++ subs ++
Enum.map(tags, fn {tag_text, tag, uuid} -> Enum.map(tags, fn {tag_text, tag, uuid} ->
url = url =
"<a data-tag='#{tag}' href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{ "<a class='hashtag' data-tag='#{tag}' href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{
tag_text tag_text
}</a>" }</a>"

View file

@ -78,14 +78,14 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
# links # links
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @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("a", ["name", "title", "class"])
# paragraphs and linebreaks # paragraphs and linebreaks
Meta.allow_tag_with_these_attributes("br", []) Meta.allow_tag_with_these_attributes("br", [])
Meta.allow_tag_with_these_attributes("p", []) Meta.allow_tag_with_these_attributes("p", [])
# microformats # microformats
Meta.allow_tag_with_these_attributes("span", []) Meta.allow_tag_with_these_attributes("span", ["class"])
# allow inline images for custom emoji # allow inline images for custom emoji
@allow_inline_images Keyword.get(@markup, :allow_inline_images) @allow_inline_images Keyword.get(@markup, :allow_inline_images)
@ -119,7 +119,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.strip_comments() Meta.strip_comments()
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @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("a", ["name", "title", "class"])
Meta.allow_tag_with_these_attributes("abbr", ["title"]) Meta.allow_tag_with_these_attributes("abbr", ["title"])
@ -134,7 +134,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
Meta.allow_tag_with_these_attributes("ol", []) Meta.allow_tag_with_these_attributes("ol", [])
Meta.allow_tag_with_these_attributes("p", []) Meta.allow_tag_with_these_attributes("p", [])
Meta.allow_tag_with_these_attributes("pre", []) Meta.allow_tag_with_these_attributes("pre", [])
Meta.allow_tag_with_these_attributes("span", []) Meta.allow_tag_with_these_attributes("span", ["class"])
Meta.allow_tag_with_these_attributes("strong", []) Meta.allow_tag_with_these_attributes("strong", [])
Meta.allow_tag_with_these_attributes("u", []) Meta.allow_tag_with_these_attributes("u", [])
Meta.allow_tag_with_these_attributes("ul", []) Meta.allow_tag_with_these_attributes("ul", [])

View file

@ -31,12 +31,15 @@ def request(method, url, body \\ "", headers \\ [], options \\ []) do
process_request_options(options) process_request_options(options)
|> process_sni_options(url) |> process_sni_options(url)
params = Keyword.get(options, :params, [])
%{} %{}
|> Builder.method(method) |> Builder.method(method)
|> Builder.headers(headers) |> Builder.headers(headers)
|> Builder.opts(options) |> Builder.opts(options)
|> Builder.url(url) |> Builder.url(url)
|> Builder.add_param(:body, :body, body) |> Builder.add_param(:body, :body, body)
|> Builder.add_param(:query, :query, params)
|> Enum.into([]) |> Enum.into([])
|> (&Tesla.request(Connection.new(), &1)).() |> (&Tesla.request(Connection.new(), &1)).()
end end

View file

@ -100,6 +100,8 @@ def add_optional_params(request, definitions, [{key, value} | tail]) do
Map Map
""" """
@spec add_param(map(), atom, atom, any()) :: map() @spec add_param(map(), atom, atom, any()) :: map()
def add_param(request, :query, :query, values), do: Map.put(request, :query, values)
def add_param(request, :body, :body, value), do: Map.put(request, :body, value) def add_param(request, :body, :body, value), do: Map.put(request, :body, value)
def add_param(request, :body, key, value) do def add_param(request, :body, key, value) do
@ -107,7 +109,10 @@ def add_param(request, :body, key, value) do
|> Map.put_new_lazy(:body, &Tesla.Multipart.new/0) |> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
|> Map.update!( |> Map.update!(
:body, :body,
&Tesla.Multipart.add_field(&1, key, Poison.encode!(value), &Tesla.Multipart.add_field(
&1,
key,
Jason.encode!(value),
headers: [{:"Content-Type", "application/json"}] headers: [{:"Content-Type", "application/json"}]
) )
) )

View file

@ -34,10 +34,11 @@ def update_stats do
peers = peers =
from( from(
u in Pleroma.User, u in Pleroma.User,
select: fragment("distinct ?->'host'", u.info), select: fragment("distinct split_part(?, '@', 2)", u.nickname),
where: u.local != ^true where: u.local != ^true
) )
|> Repo.all() |> Repo.all()
|> Enum.filter(& &1)
domain_count = Enum.count(peers) domain_count = Enum.count(peers)
@ -45,7 +46,7 @@ def update_stats do
from(u in User.local_user_query(), select: fragment("sum((?->>'note_count')::int)", u.info)) from(u in User.local_user_query(), select: fragment("sum((?->>'note_count')::int)", u.info))
status_count = Repo.one(status_query) status_count = Repo.one(status_query)
user_count = Repo.aggregate(User.local_user_query(), :count, :id) user_count = Repo.aggregate(User.active_local_user_query(), :count, :id)
Agent.update(__MODULE__, fn _ -> Agent.update(__MODULE__, fn _ ->
{peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}} {peers, %{domain_count: domain_count, status_count: status_count, user_count: user_count}}

View file

@ -34,8 +34,9 @@ defmodule Pleroma.Upload do
require Logger require Logger
@type source :: @type source ::
Plug.Upload.t() | data_uri_string :: Plug.Upload.t()
String.t() | {:from_local, name :: String.t(), id :: String.t(), path :: String.t()} | (data_uri_string :: String.t())
| {:from_local, name :: String.t(), id :: String.t(), path :: String.t()}
@type option :: @type option ::
{:type, :avatar | :banner | :background} {:type, :avatar | :banner | :background}
@ -215,6 +216,12 @@ defp tempfile_for_image(data) do
end end
defp url_from_spec(base_url, {:file, path}) do defp url_from_spec(base_url, {:file, path}) do
path =
path
|> URI.encode()
|> String.replace("?", "%3F")
|> String.replace(":", "%3A")
[base_url, "media", path] [base_url, "media", path]
|> Path.join() |> Path.join()
end end

View file

@ -822,7 +822,7 @@ def unblock_domain(user, domain) do
update_and_set_cache(cng) update_and_set_cache(cng)
end end
def local_user_query() do def local_user_query do
from( from(
u in User, u in User,
where: u.local == true, where: u.local == true,
@ -830,7 +830,14 @@ def local_user_query() do
) )
end end
def moderator_user_query() do def active_local_user_query do
from(
u in local_user_query(),
where: fragment("?->'deactivated' @> 'false'", u.info)
)
end
def moderator_user_query do
from( from(
u in User, u in User,
where: u.local == true, where: u.local == true,
@ -1066,4 +1073,14 @@ defp local_nickname_regex() do
@strict_local_nickname_regex @strict_local_nickname_regex
end end
end end
def error_user(ap_id) do
%User{
name: ap_id,
ap_id: ap_id,
info: %User.Info{},
nickname: "erroruser@example.com",
inserted_at: NaiveDateTime.utc_now()
}
end
end end

View file

@ -515,15 +515,6 @@ defp restrict_reblogs(query, %{"exclude_reblogs" => val}) when val == "true" or
defp restrict_reblogs(query, _), do: query defp restrict_reblogs(query, _), do: query
# Only search through last 100_000 activities by default
defp restrict_recent(query, %{"whole_db" => true}), do: query
defp restrict_recent(query, _) do
since = (Repo.aggregate(Activity, :max, :id) || 0) - 100_000
from(activity in query, where: activity.id > ^since)
end
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 || []
domain_blocks = info.domain_blocks || [] domain_blocks = info.domain_blocks || []
@ -574,7 +565,6 @@ def fetch_activities_query(recipients, opts \\ %{}) do
|> restrict_actor(opts) |> restrict_actor(opts)
|> restrict_type(opts) |> restrict_type(opts)
|> restrict_favorited_by(opts) |> restrict_favorited_by(opts)
|> restrict_recent(opts)
|> restrict_blocked(opts) |> restrict_blocked(opts)
|> restrict_media(opts) |> restrict_media(opts)
|> restrict_visibility(opts) |> restrict_visibility(opts)

View file

@ -54,6 +54,36 @@ def object(conn, %{"uuid" => uuid}) do
end end
end end
def object_likes(conn, %{"uuid" => uuid, "page" => page}) do
with ap_id <- o_status_url(conn, :object, uuid),
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
{_, true} <- {:public?, ActivityPub.is_public?(object)},
likes <- Utils.get_object_likes(object) do
{page, _} = Integer.parse(page)
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(ObjectView.render("likes.json", ap_id, likes, page))
else
{:public?, false} ->
{:error, :not_found}
end
end
def object_likes(conn, %{"uuid" => uuid}) do
with ap_id <- o_status_url(conn, :object, uuid),
%Object{} = object <- Object.get_cached_by_ap_id(ap_id),
{_, true} <- {:public?, ActivityPub.is_public?(object)},
likes <- Utils.get_object_likes(object) do
conn
|> put_resp_header("content-type", "application/activity+json")
|> json(ObjectView.render("likes.json", ap_id, likes))
else
{:public?, false} ->
{:error, :not_found}
end
end
def activity(conn, %{"uuid" => uuid}) do def activity(conn, %{"uuid" => uuid}) do
with ap_id <- o_status_url(conn, :activity, uuid), with ap_id <- o_status_url(conn, :activity, uuid),
%Activity{} = activity <- Activity.normalize(ap_id), %Activity{} = activity <- Activity.normalize(ap_id),
@ -204,6 +234,15 @@ def handle_user_activity(user, %{"type" => "Delete"} = params) do
end end
end end
def handle_user_activity(user, %{"type" => "Like"} = params) do
with %Object{} = object <- Object.normalize(params["object"]),
{:ok, activity, _object} <- ActivityPub.like(user, object) do
{:ok, activity}
else
_ -> {:error, "Can't like object"}
end
end
def handle_user_activity(_, _) do def handle_user_activity(_, _) do
{:error, "Unhandled activity type"} {:error, "Unhandled activity type"}
end end

View file

@ -0,0 +1,29 @@
# Pleroma: A lightweight social networking server
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ActivityPub.MRF.NoPlaceholderTextPolicy do
@behaviour Pleroma.Web.ActivityPub.MRF
@impl true
def filter(
%{
"type" => "Create",
"object" => %{"content" => content, "attachment" => _attachment} = child_object
} = object
)
when content in [".", "<p>.</p>"] do
child_object =
child_object
|> Map.put("content", "")
object =
object
|> Map.put("object", child_object)
{:ok, object}
end
@impl true
def filter(object), do: {:ok, object}
end

View file

@ -629,6 +629,7 @@ def prepare_object(object) do
|> add_mention_tags |> add_mention_tags
|> add_emoji_tags |> add_emoji_tags
|> add_attributed_to |> add_attributed_to
|> add_likes
|> prepare_attachments |> prepare_attachments
|> set_conversation |> set_conversation
|> set_reply_to_uri |> set_reply_to_uri
@ -641,7 +642,7 @@ def prepare_object(object) do
# internal -> Mastodon # internal -> Mastodon
# """ # """
def prepare_outgoing(%{"type" => "Create", "object" => %{"type" => "Note"} = object} = data) do def prepare_outgoing(%{"type" => "Create", "object" => object} = data) do
object = object =
object object
|> prepare_object |> prepare_object
@ -788,6 +789,22 @@ def add_attributed_to(object) do
|> Map.put("attributedTo", attributedTo) |> Map.put("attributedTo", attributedTo)
end end
def add_likes(%{"id" => id, "like_count" => likes} = object) do
likes = %{
"id" => "#{id}/likes",
"first" => "#{id}/likes?page=1",
"type" => "OrderedCollection",
"totalItems" => likes
}
object
|> Map.put("likes", likes)
end
def add_likes(object) do
object
end
def prepare_attachments(object) do def prepare_attachments(object) do
attachments = attachments =
(object["attachment"] || []) (object["attachment"] || [])
@ -803,7 +820,6 @@ def prepare_attachments(object) do
defp strip_internal_fields(object) do defp strip_internal_fields(object) do
object object
|> Map.drop([ |> Map.drop([
"likes",
"like_count", "like_count",
"announcements", "announcements",
"announcement_count", "announcement_count",

View file

@ -231,6 +231,27 @@ def get_existing_like(actor, %{data: %{"id" => id}}) do
Repo.one(query) Repo.one(query)
end end
@doc """
Returns like activities targeting an object
"""
def get_object_likes(%{data: %{"id" => id}}) do
query =
from(
activity in Activity,
# this is to use the index
where:
fragment(
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
activity.data,
activity.data,
^id
),
where: fragment("(?)->>'type' = 'Like'", activity.data)
)
Repo.all(query)
end
def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do def make_like_data(%User{ap_id: ap_id} = actor, %{data: %{"id" => id}} = object, activity_id) do
data = %{ data = %{
"type" => "Like", "type" => "Like",

View file

@ -35,4 +35,38 @@ def render("object.json", %{object: %Activity{} = activity}) do
Map.merge(base, additional) Map.merge(base, additional)
end end
def render("likes.json", ap_id, likes, page) do
collection(likes, "#{ap_id}/likes", page)
|> Map.merge(Pleroma.Web.ActivityPub.Utils.make_json_ld_header())
end
def render("likes.json", ap_id, likes) do
%{
"id" => "#{ap_id}/likes",
"type" => "OrderedCollection",
"totalItems" => length(likes),
"first" => collection(likes, "#{ap_id}/likes", 1)
}
|> Map.merge(Pleroma.Web.ActivityPub.Utils.make_json_ld_header())
end
def collection(collection, iri, page) do
offset = (page - 1) * 10
items = Enum.slice(collection, offset, 10)
items = Enum.map(items, fn object -> Transmogrifier.prepare_object(object.data) end)
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

@ -103,7 +103,14 @@ def post(user, %{"status" => status} = data) do
attachments, attachments,
tags, tags,
get_content_type(data["content_type"]), get_content_type(data["content_type"]),
true Enum.member?(
[true, "true"],
Map.get(
data,
"no_attachment_links",
Pleroma.Config.get([:instance, :no_attachment_links], false)
)
)
), ),
context <- make_context(inReplyTo), context <- make_context(inReplyTo),
cw <- data["spoiler_text"], cw <- data["spoiler_text"],

View file

@ -341,7 +341,6 @@ def post_status(%{assigns: %{user: user}} = conn, %{"status" => _} = params) do
params = params =
params params
|> 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)
idempotency_key = idempotency_key =
case get_req_header(conn, "idempotency-key") do case get_req_header(conn, "idempotency-key") do
@ -824,9 +823,9 @@ def account_search(%{assigns: %{user: user}} = conn, %{"q" => query} = params) d
json(conn, res) json(conn, res)
end end
def favourites(%{assigns: %{user: user}} = conn, _) do def favourites(%{assigns: %{user: user}} = conn, params) do
params = params =
%{} params
|> Map.put("type", "Create") |> Map.put("type", "Create")
|> Map.put("favorited_by", user.ap_id) |> Map.put("favorited_by", user.ap_id)
|> Map.put("blocking_user", user) |> Map.put("blocking_user", user)
@ -836,6 +835,7 @@ def favourites(%{assigns: %{user: user}} = conn, _) do
|> Enum.reverse() |> Enum.reverse()
conn conn
|> add_link_headers(:favourites, activities)
|> put_view(StatusView) |> put_view(StatusView)
|> render("index.json", %{activities: activities, for: user, as: :activity}) |> render("index.json", %{activities: activities, for: user, as: :activity})
end end

View file

@ -32,6 +32,19 @@ defp get_replied_to_activities(activities) do
end) end)
end end
defp get_user(ap_id) do
cond do
user = User.get_cached_by_ap_id(ap_id) ->
user
user = User.get_by_guessed_nickname(ap_id) ->
user
true ->
User.error_user(ap_id)
end
end
def render("index.json", opts) do def render("index.json", opts) do
replied_to_activities = get_replied_to_activities(opts.activities) replied_to_activities = get_replied_to_activities(opts.activities)
@ -48,7 +61,7 @@ def render(
"status.json", "status.json",
%{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts %{activity: %{data: %{"type" => "Announce", "object" => object}} = activity} = opts
) do ) do
user = User.get_cached_by_ap_id(activity.data["actor"]) user = get_user(activity.data["actor"])
created_at = Utils.to_masto_date(activity.data["published"]) created_at = Utils.to_masto_date(activity.data["published"])
reblogged = Activity.get_create_activity_by_object_ap_id(object) reblogged = Activity.get_create_activity_by_object_ap_id(object)
@ -93,7 +106,7 @@ def render(
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 = get_user(activity.data["actor"])
like_count = object["like_count"] || 0 like_count = object["like_count"] || 0
announcement_count = object["announcement_count"] || 0 announcement_count = object["announcement_count"] || 0
@ -116,7 +129,7 @@ def render("status.json", %{activity: %{data: %{"object" => object}} = activity}
created_at = Utils.to_masto_date(object["published"]) created_at = Utils.to_masto_date(object["published"])
reply_to = get_reply_to(activity, opts) reply_to = get_reply_to(activity, opts)
reply_to_user = reply_to && User.get_cached_by_ap_id(reply_to.data["actor"]) reply_to_user = reply_to && get_user(reply_to.data["actor"])
content = content =
object object

View file

@ -1,5 +1,9 @@
defmodule Pleroma.Web.RichMedia.Parser do defmodule Pleroma.Web.RichMedia.Parser do
@parsers [Pleroma.Web.RichMedia.Parsers.OGP, Pleroma.Web.RichMedia.Parsers.TwitterCard] @parsers [
Pleroma.Web.RichMedia.Parsers.OGP,
Pleroma.Web.RichMedia.Parsers.TwitterCard,
Pleroma.Web.RichMedia.Parsers.OEmbed
]
if Mix.env() == :test do if Mix.env() == :test do
def parse(url), do: parse_url(url) def parse(url), do: parse_url(url)

View file

@ -0,0 +1,27 @@
defmodule Pleroma.Web.RichMedia.Parsers.OEmbed do
def parse(html, _data) do
with elements = [_ | _] <- get_discovery_data(html),
{:ok, oembed_url} <- get_oembed_url(elements),
{:ok, oembed_data} <- get_oembed_data(oembed_url) do
{:ok, oembed_data}
else
_e -> {:error, "No OEmbed data found"}
end
end
defp get_discovery_data(html) do
html |> Floki.find("link[type='application/json+oembed']")
end
defp get_oembed_url(nodes) do
{"link", attributes, _children} = nodes |> hd()
{:ok, Enum.into(attributes, %{})["href"]}
end
defp get_oembed_data(url) do
{:ok, %Tesla.Env{body: json}} = Pleroma.HTTP.get(url)
{:ok, Poison.decode!(json)}
end
end

View file

@ -431,6 +431,7 @@ defmodule Pleroma.Web.Router do
get("/users/:nickname/followers", ActivityPubController, :followers) get("/users/:nickname/followers", ActivityPubController, :followers)
get("/users/:nickname/following", ActivityPubController, :following) get("/users/:nickname/following", ActivityPubController, :following)
get("/users/:nickname/outbox", ActivityPubController, :outbox) get("/users/:nickname/outbox", ActivityPubController, :outbox)
get("/objects/:uuid/likes", ActivityPubController, :object_likes)
end end
pipeline :activitypub_client do pipeline :activitypub_client do

View file

@ -101,20 +101,10 @@ defp get_user(ap_id, opts) do
user user
true -> true ->
error_user(ap_id) User.error_user(ap_id)
end end
end end
defp error_user(ap_id) do
%User{
name: ap_id,
ap_id: ap_id,
info: %User.Info{},
nickname: "erroruser@example.com",
inserted_at: NaiveDateTime.utc_now()
}
end
def render("index.json", opts) do def render("index.json", opts) do
context_ids = collect_context_ids(opts.activities) context_ids = collect_context_ids(opts.activities)
users = collect_users(opts.activities) users = collect_users(opts.activities)

View file

@ -121,6 +121,12 @@ def incoming_subscription_request(user, %{"hub.mode" => "subscribe"} = params) d
end end
end end
def incoming_subscription_request(user, params) do
Logger.info("Unhandled WebSub request for #{user.nickname}: #{inspect(params)}")
{:error, "Invalid WebSub request"}
end
defp get_subscription(topic, callback) do defp get_subscription(topic, callback) do
Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) || Repo.get_by(WebsubServerSubscription, topic: topic, callback: callback) ||
%WebsubServerSubscription{} %WebsubServerSubscription{}

View file

@ -67,6 +67,13 @@ def websub_subscription_confirmation(
end end
end end
def websub_subscription_confirmation(conn, params) do
Logger.info("Invalid WebSub confirmation request: #{inspect(params)}")
conn
|> send_resp(500, "Invalid parameters")
end
def websub_incoming(conn, %{"id" => id}) do def websub_incoming(conn, %{"id" => id}) do
with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")), with "sha1=" <> signature <- hd(get_req_header(conn, "x-hub-signature")),
signature <- String.downcase(signature), signature <- String.downcase(signature),

3
test/fixtures/rich_media/oembed.html vendored Normal file
View file

@ -0,0 +1,3 @@
<link rel="alternate" type="application/json+oembed"
href="http://example.com/oembed.json"
title="Bacon Lollys oEmbed Profile" />

1
test/fixtures/rich_media/oembed.json vendored Normal file
View file

@ -0,0 +1 @@
{"type":"photo","flickr_type":"photo","title":"Bacon Lollys","author_name":"\u202e\u202d\u202cbees\u202c","author_url":"https:\/\/www.flickr.com\/photos\/bees\/","width":"1024","height":"768","url":"https:\/\/farm4.staticflickr.com\/3040\/2362225867_4a87ab8baf_b.jpg","web_page":"https:\/\/www.flickr.com\/photos\/bees\/2362225867\/","thumbnail_url":"https:\/\/farm4.staticflickr.com\/3040\/2362225867_4a87ab8baf_q.jpg","thumbnail_width":150,"thumbnail_height":150,"web_page_short_url":"https:\/\/flic.kr\/p\/4AK2sc","license":"All Rights Reserved","license_id":0,"html":"<a data-flickr-embed=\"true\" href=\"https:\/\/www.flickr.com\/photos\/bees\/2362225867\/\" title=\"Bacon Lollys by \u202e\u202d\u202cbees\u202c, on Flickr\"><img src=\"https:\/\/farm4.staticflickr.com\/3040\/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"><\/a><script async src=\"https:\/\/embedr.flickr.com\/assets\/client-code.js\" charset=\"utf-8\"><\/script>","version":"1.0","cache_age":3600,"provider_name":"Flickr","provider_url":"https:\/\/www.flickr.com\/"}

View file

@ -19,7 +19,7 @@ test "turns hashtags into links" do
text = "I love #cofe and #2hu" text = "I love #cofe and #2hu"
expected_text = expected_text =
"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>" "I love <a class='hashtag' data-tag='cofe' href='http://localhost:4001/tag/cofe' rel='tag'>#cofe</a> and <a class='hashtag' data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a>"
tags = Formatter.parse_tags(text) tags = Formatter.parse_tags(text)
@ -31,7 +31,7 @@ test "does not turn html characters to tags" do
text = "Fact #3: pleroma does what mastodon't" text = "Fact #3: pleroma does what mastodon't"
expected_text = expected_text =
"Fact <a data-tag='3' href='http://localhost:4001/tag/3' rel='tag'>#3</a>: pleroma does what mastodon't" "Fact <a class='hashtag' data-tag='3' href='http://localhost:4001/tag/3' rel='tag'>#3</a>: pleroma does what mastodon't"
tags = Formatter.parse_tags(text) tags = Formatter.parse_tags(text)
@ -144,11 +144,13 @@ test "gives a replacement for user links" do
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
expected_text = expected_text =
"<span><a data-user='#{gsimg.id}' class='mention' href='#{gsimg.ap_id}'>@<span>gsimg</span></a></span> According to <span><a data-user='#{ "<span class='h-card'><a data-user='#{gsimg.id}' class='u-url mention' href='#{
gsimg.ap_id
}'>@<span>gsimg</span></a></span> According to <span class='h-card'><a data-user='#{
archaeme.id archaeme.id
}' class='mention' href='#{"https://archeme/@archa_eme_"}'>@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span><a data-user='#{ }' class='u-url mention' href='#{"https://archeme/@archa_eme_"}'>@<span>archa_eme_</span></a></span>, that is @daggsy. Also hello <span class='h-card'><a data-user='#{
archaeme_remote.id archaeme_remote.id
}' class='mention' href='#{archaeme_remote.ap_id}'>@<span>archaeme</span></a></span>" }' class='u-url mention' href='#{archaeme_remote.ap_id}'>@<span>archaeme</span></a></span>"
assert expected_text == Formatter.finalize({subs, text}) assert expected_text == Formatter.finalize({subs, text})
end end
@ -166,7 +168,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) Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
expected_text = expected_text =
"<span><a data-user='#{mike.id}' class='mention' href='#{mike.ap_id}'>@<span>mike</span></a></span> test" "<span class='h-card'><a data-user='#{mike.id}' class='u-url mention' href='#{mike.ap_id}'>@<span>mike</span></a></span> test"
assert expected_text == Formatter.finalize({subs, text}) assert expected_text == Formatter.finalize({subs, text})
end end
@ -183,7 +185,7 @@ test "gives a replacement for single-character local nicknames" do
Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end) Enum.each(subs, fn {uuid, _} -> assert String.contains?(text, uuid) end)
expected_text = expected_text =
"<span><a data-user='#{o.id}' class='mention' href='#{o.ap_id}'>@<span>o</span></a></span> hi" "<span class='h-card'><a data-user='#{o.id}' class='u-url mention' href='#{o.ap_id}'>@<span>o</span></a></span> hi"
assert expected_text == Formatter.finalize({subs, text}) assert expected_text == Formatter.finalize({subs, text})
end end

View file

@ -57,6 +57,11 @@ def direct_note_factory do
%Pleroma.Object{data: Map.merge(data, %{"to" => [user2.ap_id]})} %Pleroma.Object{data: Map.merge(data, %{"to" => [user2.ap_id]})}
end end
def article_factory do
note_factory()
|> Map.put("type", "Article")
end
def tombstone_factory do def tombstone_factory do
data = %{ data = %{
"type" => "Tombstone", "type" => "Tombstone",
@ -110,6 +115,26 @@ def note_activity_factory do
} }
end end
def article_activity_factory do
article = insert(:article)
data = %{
"id" => Pleroma.Web.ActivityPub.Utils.generate_activity_id(),
"type" => "Create",
"actor" => article.data["actor"],
"to" => article.data["to"],
"object" => article.data,
"published" => DateTime.utc_now() |> DateTime.to_iso8601(),
"context" => article.data["context"]
}
%Pleroma.Activity{
data: data,
actor: data["actor"],
recipients: data["to"]
}
end
def announce_activity_factory do def announce_activity_factory do
note_activity = insert(:note_activity) note_activity = insert(:note_activity)
user = insert(:user) user = insert(:user)

View file

@ -137,5 +137,35 @@ test "copies the file to the configured folder with anonymizing filename" do
refute data["name"] == "an [image.jpg" refute data["name"] == "an [image.jpg"
end end
test "escapes invalid characters in url" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "an… image.jpg"
}
{:ok, data} = Upload.store(file)
[attachment_url | _] = data["url"]
assert Path.basename(attachment_url["href"]) == "an%E2%80%A6%20image.jpg"
end
test "replaces : (colon) and ? (question-mark) to %3A and %3F (respectively)" do
File.cp!("test/fixtures/image.jpg", "test/fixtures/image_tmp.jpg")
file = %Plug.Upload{
content_type: "image/jpg",
path: Path.absname("test/fixtures/image_tmp.jpg"),
filename: "is:an?image.jpg"
}
{:ok, data} = Upload.store(file)
[attachment_url | _] = data["url"]
assert Path.basename(attachment_url["href"]) == "is%3Aan%3Fimage.jpg"
end
end end
end end

View file

@ -89,6 +89,21 @@ test "it returns 404 for tombstone objects", %{conn: conn} do
end end
end end
describe "/object/:uuid/likes" do
test "it returns the like activities in a collection", %{conn: conn} do
like = insert(:like_activity)
uuid = String.split(like.data["object"], "/") |> List.last()
result =
conn
|> put_req_header("accept", "application/activity+json")
|> get("/objects/#{uuid}/likes")
|> json_response(200)
assert List.first(result["first"]["orderedItems"])["id"] == like.data["id"]
end
end
describe "/activities/:uuid" do describe "/activities/:uuid" do
test "it returns a json representation of the activity", %{conn: conn} do test "it returns a json representation of the activity", %{conn: conn} do
activity = insert(:note_activity) activity = insert(:note_activity)
@ -292,6 +307,31 @@ test "it rejects delete activity of object from other actor", %{conn: conn} do
assert json_response(conn, 400) assert json_response(conn, 400)
end end
test "it increases like count when receiving a like action", %{conn: conn} do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
data = %{
type: "Like",
object: %{
id: note_activity.data["object"]["id"]
}
}
conn =
conn
|> assign(:user, user)
|> put_req_header("content-type", "application/activity+json")
|> post("/users/#{user.nickname}/outbox", data)
result = json_response(conn, 201)
assert Activity.get_by_ap_id(result["id"])
object = Object.get_by_ap_id(note_activity.data["object"]["id"])
assert object
assert object.data["like_count"] == 1
end
end end
describe "/users/:nickname/followers" do describe "/users/:nickname/followers" do

View file

@ -829,12 +829,33 @@ test "it strips internal fields" do
assert length(modified["object"]["tag"]) == 2 assert length(modified["object"]["tag"]) == 2
assert is_nil(modified["object"]["emoji"]) assert is_nil(modified["object"]["emoji"])
assert is_nil(modified["object"]["likes"])
assert is_nil(modified["object"]["like_count"]) assert is_nil(modified["object"]["like_count"])
assert is_nil(modified["object"]["announcements"]) assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"]) assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"]) assert is_nil(modified["object"]["context_id"])
end end
test "it strips internal fields of article" do
activity = insert(:article_activity)
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
assert length(modified["object"]["tag"]) == 2
assert is_nil(modified["object"]["emoji"])
assert is_nil(modified["object"]["like_count"])
assert is_nil(modified["object"]["announcements"])
assert is_nil(modified["object"]["announcement_count"])
assert is_nil(modified["object"]["context_id"])
end
test "it adds like collection to object" do
activity = insert(:note_activity)
{:ok, modified} = Transmogrifier.prepare_outgoing(activity.data)
assert modified["object"]["likes"]["type"] == "OrderedCollection"
assert modified["object"]["likes"]["totalItems"] == 0
end
end end
describe "user upgrade" do describe "user upgrade" do

View file

@ -386,7 +386,7 @@ test "get a filter", %{conn: conn} do
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/filters/#{filter.filter_id}") |> get("/api/v1/filters/#{filter.filter_id}")
assert response = json_response(conn, 200) assert _response = json_response(conn, 200)
end end
test "update a filter", %{conn: conn} do test "update a filter", %{conn: conn} do
@ -600,7 +600,9 @@ test "list of notifications", %{conn: conn} do
|> get("/api/v1/notifications") |> get("/api/v1/notifications")
expected_response = expected_response =
"hi <span><a data-user=\"#{user.id}\" href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>" "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
user.ap_id
}\">@<span>#{user.nickname}</span></a></span>"
assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200) assert [%{"status" => %{"content" => response}} | _rest] = json_response(conn, 200)
assert response == expected_response assert response == expected_response
@ -621,7 +623,9 @@ test "getting a single notification", %{conn: conn} do
|> get("/api/v1/notifications/#{notification.id}") |> get("/api/v1/notifications/#{notification.id}")
expected_response = expected_response =
"hi <span><a data-user=\"#{user.id}\" href=\"#{user.ap_id}\">@<span>#{user.nickname}</span></a></span>" "hi <span class=\"h-card\"><a data-user=\"#{user.id}\" class=\"u-url mention\" href=\"#{
user.ap_id
}\">@<span>#{user.nickname}</span></a></span>"
assert %{"status" => %{"content" => response}} = json_response(conn, 200) assert %{"status" => %{"content" => response}} = json_response(conn, 200)
assert response == expected_response assert response == expected_response
@ -1349,13 +1353,42 @@ test "returns the favorites of a user", %{conn: conn} do
{:ok, _, _} = CommonAPI.favorite(activity.id, user) {:ok, _, _} = CommonAPI.favorite(activity.id, user)
conn = first_conn =
conn conn
|> assign(:user, user) |> assign(:user, user)
|> get("/api/v1/favourites") |> get("/api/v1/favourites")
assert [status] = json_response(conn, 200) assert [status] = json_response(first_conn, 200)
assert status["id"] == to_string(activity.id) assert status["id"] == to_string(activity.id)
assert [{"link", _link_header}] =
Enum.filter(first_conn.resp_headers, fn element -> match?({"link", _}, element) end)
# Honours query params
{:ok, second_activity} =
CommonAPI.post(other_user, %{
"status" =>
"Trees Are Never Sad Look At Them Every Once In Awhile They're Quite Beautiful."
})
{:ok, _, _} = CommonAPI.favorite(second_activity.id, user)
last_like = status["id"]
second_conn =
conn
|> assign(:user, user)
|> get("/api/v1/favourites?since_id=#{last_like}")
assert [second_status] = json_response(second_conn, 200)
assert second_status["id"] == to_string(second_activity.id)
third_conn =
conn
|> assign(:user, user)
|> get("/api/v1/favourites?limit=0")
assert [] = json_response(third_conn, 200)
end end
describe "updating credentials" do describe "updating credentials" do
@ -1373,9 +1406,9 @@ test "updates the user's bio", %{conn: conn} do
assert user = json_response(conn, 200) assert user = json_response(conn, 200)
assert user["note"] == assert user["note"] ==
"I drink <a data-tag=\"cofe\" href=\"http://localhost:4001/tag/cofe\">#cofe</a> with <span><a data-user=\"#{ "I drink <a class=\"hashtag\" data-tag=\"cofe\" href=\"http://localhost:4001/tag/cofe\">#cofe</a> with <span class=\"h-card\"><a data-user=\"#{
user2.id user2.id
}\" href=\"#{user2.ap_id}\">@<span>#{user2.nickname}</span></a></span>" }\" class=\"u-url mention\" href=\"#{user2.ap_id}\">@<span>#{user2.nickname}</span></a></span>"
end end
test "updates the user's locking status", %{conn: conn} do test "updates the user's locking status", %{conn: conn} do
@ -1440,22 +1473,41 @@ test "updates the user's banner", %{conn: conn} do
end end
test "get instance information", %{conn: conn} do test "get instance information", %{conn: conn} do
insert(:user, %{local: true})
user = insert(:user, %{local: true}) user = insert(:user, %{local: true})
insert(:user, %{local: false})
user2 = insert(:user, %{local: true})
{:ok, _user2} = User.deactivate(user2, !user2.info.deactivated)
insert(:user, %{local: false, nickname: "u@peer1.com"})
insert(:user, %{local: false, nickname: "u@peer2.com"})
{:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"}) {:ok, _} = TwitterAPI.create_status(user, %{"status" => "cofe"})
Pleroma.Stats.update_stats() Pleroma.Stats.update_stats()
conn = conn = get(conn, "/api/v1/instance")
conn
|> get("/api/v1/instance")
assert result = json_response(conn, 200) assert result = json_response(conn, 200)
assert result["stats"]["user_count"] == 2 stats = result["stats"]
assert result["stats"]["status_count"] == 1
assert stats
assert stats["user_count"] == 1
assert stats["status_count"] == 1
assert stats["domain_count"] == 2
end
test "get peers", %{conn: conn} do
insert(:user, %{local: false, nickname: "u@peer1.com"})
insert(:user, %{local: false, nickname: "u@peer2.com"})
Pleroma.Stats.update_stats()
conn = get(conn, "/api/v1/instance/peers")
assert result = json_response(conn, 200)
assert ["peer1.com", "peer2.com"] == Enum.sort(result)
end end
test "put settings", %{conn: conn} do test "put settings", %{conn: conn} do
@ -1466,7 +1518,7 @@ test "put settings", %{conn: conn} do
|> assign(:user, user) |> assign(:user, user)
|> put("/api/web/settings", %{"data" => %{"programming" => "socks"}}) |> put("/api/web/settings", %{"data" => %{"programming" => "socks"}})
assert result = json_response(conn, 200) assert _result = json_response(conn, 200)
user = User.get_cached_by_ap_id(user.ap_id) user = User.get_cached_by_ap_id(user.ap_id)
assert user.info.settings == %{"programming" => "socks"} assert user.info.settings == %{"programming" => "socks"}

View file

@ -19,6 +19,36 @@ defmodule Pleroma.Web.MastodonAPI.StatusViewTest do
:ok :ok
end end
test "returns a temporary ap_id based user for activities missing db users" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
Repo.delete(user)
Cachex.clear(:user_cache)
%{account: ms_user} = StatusView.render("status.json", activity: activity)
assert ms_user.acct == "erroruser@example.com"
end
test "tries to get a user by nickname if fetching by ap_id doesn't work" do
user = insert(:user)
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
{:ok, user} =
user
|> Ecto.Changeset.change(%{ap_id: "#{user.ap_id}/extension/#{user.nickname}"})
|> Repo.update()
Cachex.clear(:user_cache)
result = StatusView.render("status.json", activity: activity)
assert result[:account][:id] == to_string(user.id)
end
test "a note with null content" do test "a note with null content" do
note = insert(:note_activity) note = insert(:note_activity)

View file

@ -15,6 +15,18 @@ defmodule Pleroma.Web.RichMedia.ParserTest do
} -> } ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")} %Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/twitter_card.html")}
%{
method: :get,
url: "http://example.com/oembed"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.html")}
%{
method: :get,
url: "http://example.com/oembed.json"
} ->
%Tesla.Env{status: 200, body: File.read!("test/fixtures/rich_media/oembed.json")}
%{method: :get, url: "http://example.com/empty"} -> %{method: :get, url: "http://example.com/empty"} ->
%Tesla.Env{status: 200, body: "hello"} %Tesla.Env{status: 200, body: "hello"}
end) end)
@ -48,4 +60,33 @@ test "parses twitter card" do
description: "View the album on Flickr." description: "View the album on Flickr."
}} }}
end end
test "parses OEmbed" do
assert Pleroma.Web.RichMedia.Parser.parse("http://example.com/oembed") ==
{:ok,
%{
"author_name" => "bees",
"author_url" => "https://www.flickr.com/photos/bees/",
"cache_age" => 3600,
"flickr_type" => "photo",
"height" => "768",
"html" =>
"<a data-flickr-embed=\"true\" href=\"https://www.flickr.com/photos/bees/2362225867/\" title=\"Bacon Lollys by bees, on Flickr\"><img src=\"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg\" width=\"1024\" height=\"768\" alt=\"Bacon Lollys\"></a><script async src=\"https://embedr.flickr.com/assets/client-code.js\" charset=\"utf-8\"></script>",
"license" => "All Rights Reserved",
"license_id" => 0,
"provider_name" => "Flickr",
"provider_url" => "https://www.flickr.com/",
"thumbnail_height" => 150,
"thumbnail_url" =>
"https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_q.jpg",
"thumbnail_width" => 150,
"title" => "Bacon Lollys",
"type" => "photo",
"url" => "https://farm4.staticflickr.com/3040/2362225867_4a87ab8baf_b.jpg",
"version" => "1.0",
"web_page" => "https://www.flickr.com/photos/bees/2362225867/",
"web_page_short_url" => "https://flic.kr/p/4AK2sc",
"width" => "1024"
}}
end
end end

View file

@ -1357,9 +1357,9 @@ test "it updates a user's profile", %{conn: conn} do
assert user.name == "new name" assert user.name == "new name"
assert user.bio == assert user.bio ==
"hi <span><a data-user='#{user2.id}' class='mention' href='#{user2.ap_id}'>@<span>#{ "hi <span class='h-card'><a data-user='#{user2.id}' class='u-url mention' href='#{
user2.nickname user2.ap_id
}</span></a></span>" }'>@<span>#{user2.nickname}</span></a></span>"
assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user}) assert json_response(conn, 200) == UserView.render("user.json", %{user: user, for: user})
end end

View file

@ -38,7 +38,7 @@ test "create a status" do
{:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input) {:ok, activity = %Activity{}} = TwitterAPI.create_status(user, input)
expected_text = expected_text =
"Hello again, <span><a data-user='#{mentioned_user.id}' class='mention' href='shp'>@<span>shp</span></a></span>.&lt;script&gt;&lt;/script&gt;<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>" "Hello again, <span class='h-card'><a data-user='#{mentioned_user.id}' class='u-url mention' href='shp'>@<span>shp</span></a></span>.&lt;script&gt;&lt;/script&gt;<br>This is on another :moominmamma: line. <a class='hashtag' data-tag='2hu' href='http://localhost:4001/tag/2hu' rel='tag'>#2hu</a> <a class='hashtag' data-tag='epic' href='http://localhost:4001/tag/epic' rel='tag'>#epic</a> <a class='hashtag' 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", "content"]) == expected_text
assert get_in(activity.data, ["object", "type"]) == "Note" assert get_in(activity.data, ["object", "type"]) == "Note"
@ -328,7 +328,7 @@ test "it registers a new user and parses mentions in the bio" do
{:ok, user2} = TwitterAPI.register_user(data2) {:ok, user2} = TwitterAPI.register_user(data2)
expected_text = expected_text =
"<span><a data-user='#{user1.id}' class='mention' href='#{user1.ap_id}'>@<span>john</span></a></span> test" "<span class='h-card'><a data-user='#{user1.id}' class='u-url mention' href='#{user1.ap_id}'>@<span>john</span></a></span> test"
assert user2.bio == expected_text assert user2.bio == expected_text
end end

View file

@ -66,7 +66,7 @@ test "a create activity with a html status" do
result = ActivityView.render("activity.json", activity: activity) result = ActivityView.render("activity.json", activity: activity)
assert result["statusnet_html"] == assert result["statusnet_html"] ==
"<a data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\">#cycling</a> <a data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\">#CHScycling</a> <a data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\">#commute</a><br />MVIMG_20181211_054020.jpg" "<a class=\"hashtag\" data-tag=\"bike\" href=\"http://localhost:4001/tag/bike\">#Bike</a> log - Commute Tuesday<br /><a href=\"https://pla.bike/posts/20181211/\">https://pla.bike/posts/20181211/</a><br /><a class=\"hashtag\" data-tag=\"cycling\" href=\"http://localhost:4001/tag/cycling\">#cycling</a> <a class=\"hashtag\" data-tag=\"chscycling\" href=\"http://localhost:4001/tag/chscycling\">#CHScycling</a> <a class=\"hashtag\" data-tag=\"commute\" href=\"http://localhost:4001/tag/commute\">#commute</a><br />MVIMG_20181211_054020.jpg"
assert result["text"] == assert result["text"] ==
"#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg" "#Bike log - Commute Tuesday\nhttps://pla.bike/posts/20181211/\n#cycling #CHScycling #commute\nMVIMG_20181211_054020.jpg"
@ -141,7 +141,9 @@ test "a create activity with a note" do
"summary" => "", "summary" => "",
"summary_html" => "", "summary_html" => "",
"statusnet_html" => "statusnet_html" =>
"Hey <span><a data-user=\"#{other_user.id}\" href=\"#{other_user.ap_id}\">@<span>shp</span></a></span>!", "Hey <span class=\"h-card\"><a data-user=\"#{other_user.id}\" class=\"u-url mention\" href=\"#{
other_user.ap_id
}\">@<span>shp</span></a></span>!",
"tags" => [], "tags" => [],
"text" => "Hey @shp!", "text" => "Hey @shp!",
"uri" => activity.data["object"]["id"], "uri" => activity.data["object"]["id"],