...
 
Commits (109)
...@@ -16,6 +16,8 @@ config :pleroma, Pleroma.Upload, ...@@ -16,6 +16,8 @@ config :pleroma, Pleroma.Upload,
config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png"] config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png"]
config :pleroma, :uri_schemes, additionnal_schemes: []
# Configures the endpoint # Configures the endpoint
config :pleroma, Pleroma.Web.Endpoint, config :pleroma, Pleroma.Web.Endpoint,
url: [host: "localhost"], url: [host: "localhost"],
...@@ -75,7 +77,8 @@ config :pleroma, :fe, ...@@ -75,7 +77,8 @@ config :pleroma, :fe,
who_to_follow_provider: who_to_follow_provider:
"https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-osa-api.cgi?{{host}}+{{user}}", "https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-osa-api.cgi?{{host}}+{{user}}",
who_to_follow_link: "https://vinayaka.distsn.org/?{{host}}+{{user}}", who_to_follow_link: "https://vinayaka.distsn.org/?{{host}}+{{user}}",
scope_options_enabled: false scope_options_enabled: false,
collapse_message_with_subject: false
config :pleroma, :activitypub, config :pleroma, :activitypub,
accept_blocks: true, accept_blocks: true,
...@@ -124,6 +127,13 @@ config :pleroma, :gopher, ...@@ -124,6 +127,13 @@ config :pleroma, :gopher,
ip: {0, 0, 0, 0}, ip: {0, 0, 0, 0},
port: 9999 port: 9999
config :pleroma, :suggestions,
enabled: false,
third_party_engine:
"http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-suggestions-api.cgi?{{host}}+{{user}}",
timeout: 300_000,
web: "https://vinayaka.distsn.org/?{{host}}+{{user}}"
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"
#!/sbin/openrc-run
# Requires OpenRC >= 0.35
directory=~pleroma/pleroma
command=/usr/bin/mix
command_args="phx.server"
command_user=pleroma:pleroma
command_background=1
export PORT=4000
export MIX_ENV=prod
# Ask process to terminate within 30 seconds, otherwise kill it
retry="SIGTERM/30 SIGKILL/5"
pidfile="/var/run/pleroma.pid"
depend() {
need nginx postgresql
}
\ No newline at end of file
...@@ -16,7 +16,7 @@ defmodule Pleroma.Formatter do ...@@ -16,7 +16,7 @@ defmodule Pleroma.Formatter do
def parse_mentions(text) do def parse_mentions(text) do
# Modified from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address # Modified from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address
regex = regex =
~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u ~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]*@?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u
Regex.scan(regex, text) Regex.scan(regex, text)
|> List.flatten() |> List.flatten()
...@@ -165,8 +165,29 @@ defmodule Pleroma.Formatter do ...@@ -165,8 +165,29 @@ defmodule Pleroma.Formatter do
@emoji @emoji
end end
@link_regex ~r/https?:\/\/[\w\.\/?=\-#\+%&@~'\(\):]+[\w\/]/u @link_regex ~r/[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+/ui
# IANA got a list https://www.iana.org/assignments/uri-schemes/ but
# Stuff like ipfs isn’t in it
# There is very niche stuff
@uri_schemes [
"https://",
"http://",
"dat://",
"dweb://",
"gopher://",
"ipfs://",
"ipns://",
"irc:",
"ircs:",
"magnet:",
"mailto:",
"mumble:",
"ssb://",
"xmpp:"
]
# TODO: make it use something other than @link_regex
def html_escape(text) do def html_escape(text) do
Regex.split(@link_regex, text, include_captures: true) Regex.split(@link_regex, text, include_captures: true)
|> Enum.map_every(2, fn chunk -> |> Enum.map_every(2, fn chunk ->
...@@ -176,11 +197,18 @@ defmodule Pleroma.Formatter do ...@@ -176,11 +197,18 @@ defmodule Pleroma.Formatter do
|> Enum.join("") |> Enum.join("")
end end
@doc "changes http:... links to html links" @doc "changes scheme:... urls to html links"
def add_links({subs, text}) do def add_links({subs, text}) do
additionnal_schemes =
Application.get_env(:pleroma, :uri_schemes, [])
|> Keyword.get(:additionnal_schemes, [])
links = links =
Regex.scan(@link_regex, text) text
|> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end) |> String.split([" ", "\t", "<br>"])
|> Enum.filter(fn word -> String.starts_with?(word, @uri_schemes ++ additionnal_schemes) end)
|> Enum.filter(fn word -> Regex.match?(@link_regex, word) end)
|> Enum.map(fn url -> {Ecto.UUID.generate(), url} end)
|> Enum.sort_by(fn {_, url} -> -String.length(url) end) |> Enum.sort_by(fn {_, url} -> -String.length(url) end)
uuid_text = uuid_text =
......
defmodule Pleroma.HTTP do defmodule Pleroma.HTTP do
use HTTPoison.Base require HTTPoison
def request(method, url, body \\ "", headers \\ [], options \\ []) do
options =
process_request_options(options)
|> process_sni_options(url)
HTTPoison.request(method, url, body, headers, options)
end
defp process_sni_options(options, url) do
uri = URI.parse(url)
host = uri.host |> to_charlist()
case uri.scheme do
"https" -> options ++ [ssl: [server_name_indication: host]]
_ -> options
end
end
def process_request_options(options) do def process_request_options(options) do
config = Application.get_env(:pleroma, :http, []) config = Application.get_env(:pleroma, :http, [])
...@@ -10,4 +28,9 @@ defmodule Pleroma.HTTP do ...@@ -10,4 +28,9 @@ defmodule Pleroma.HTTP do
_ -> options ++ [proxy: proxy] _ -> options ++ [proxy: proxy]
end end
end end
def get(url, headers \\ [], options \\ []), do: request(:get, url, "", headers, options)
def post(url, body, headers \\ [], options \\ []),
do: request(:post, url, body, headers, options)
end end
...@@ -126,20 +126,20 @@ defmodule Pleroma.Upload do ...@@ -126,20 +126,20 @@ defmodule Pleroma.Upload do
if should_dedupe do if should_dedupe do
create_name(uuid, List.last(String.split(file.filename, ".")), type) create_name(uuid, List.last(String.split(file.filename, ".")), type)
else else
unless String.contains?(file.filename, ".") do parts = String.split(file.filename, ".")
case type do
"image/png" -> file.filename <> ".png" new_filename =
"image/jpeg" -> file.filename <> ".jpg" if length(parts) > 1 do
"image/gif" -> file.filename <> ".gif" Enum.drop(parts, -1) |> Enum.join(".")
"video/webm" -> file.filename <> ".webm" else
"video/mp4" -> file.filename <> ".mp4" Enum.join(parts)
"audio/mpeg" -> file.filename <> ".mp3"
"audio/ogg" -> file.filename <> ".ogg"
"audio/wav" -> file.filename <> ".wav"
_ -> file.filename
end end
else
file.filename case type do
"application/octet-stream" -> file.filename
"audio/mpeg" -> new_filename <> ".mp3"
"image/jpeg" -> new_filename <> ".jpg"
_ -> Enum.join([new_filename, String.split(type, "/") |> List.last()], ".")
end end
end end
end end
......
...@@ -567,7 +567,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do ...@@ -567,7 +567,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_and_prepare_user_from_ap_id(ap_id) do def fetch_and_prepare_user_from_ap_id(ap_id) do
with {:ok, %{status_code: 200, body: body}} <- with {:ok, %{status_code: 200, body: body}} <-
@httpoison.get(ap_id, Accept: "application/activity+json"), @httpoison.get(ap_id, [Accept: "application/activity+json"], follow_redirect: true),
{:ok, data} <- Jason.decode(body) do {:ok, data} <- Jason.decode(body) do
user_data_from_user_object(data) user_data_from_user_object(data)
else else
......
...@@ -18,12 +18,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ...@@ -18,12 +18,16 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
def get_actor(%{"actor" => actor}) when is_list(actor) do def get_actor(%{"actor" => actor}) when is_list(actor) do
Enum.at(actor, 0) if is_binary(Enum.at(actor, 0)) do
Enum.at(actor, 0)
else
Enum.find(actor, fn %{"type" => type} -> type == "Person" end)
|> Map.get("id")
end
end end
def get_actor(%{"actor" => actor_list}) do def get_actor(%{"actor" => actor}) when is_map(actor) do
Enum.find(actor_list, fn %{"type" => type} -> type == "Person" end) actor["id"]
|> Map.get("id")
end end
@doc """ @doc """
...@@ -38,6 +42,25 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ...@@ -38,6 +42,25 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> fix_emoji |> fix_emoji
|> fix_tag |> fix_tag
|> fix_content_map |> fix_content_map
|> fix_likes
|> fix_addressing
end
def fix_addressing_list(map, field) do
if is_binary(map[field]) do
map
|> Map.put(field, [map[field]])
else
map
end
end
def fix_addressing(map) do
map
|> fix_addressing_list("to")
|> fix_addressing_list("cc")
|> fix_addressing_list("bto")
|> fix_addressing_list("bcc")
end end
def fix_actor(%{"attributedTo" => actor} = object) do def fix_actor(%{"attributedTo" => actor} = object) do
...@@ -45,6 +68,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ...@@ -45,6 +68,20 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> Map.put("actor", get_actor(%{"actor" => actor})) |> Map.put("actor", get_actor(%{"actor" => actor}))
end end
def fix_likes(%{"likes" => likes} = object)
when is_bitstring(likes) do
# Check for standardisation
# This is what Peertube does
# curl -H 'Accept: application/activity+json' $likes | jq .totalItems
object
|> Map.put("likes", [])
|> Map.put("like_count", 0)
end
def fix_likes(object) do
object
end
def fix_in_reply_to(%{"inReplyTo" => in_reply_to_id} = object) def fix_in_reply_to(%{"inReplyTo" => in_reply_to_id} = object)
when not is_nil(in_reply_to_id) do when not is_nil(in_reply_to_id) do
case ActivityPub.fetch_object_from_id(in_reply_to_id) do case ActivityPub.fetch_object_from_id(in_reply_to_id) do
...@@ -72,8 +109,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ...@@ -72,8 +109,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_in_reply_to(object), do: object def fix_in_reply_to(object), do: object
def fix_context(object) do def fix_context(object) do
context = object["context"] || object["conversation"] || Utils.generate_context_id()
object object
|> Map.put("context", object["conversation"]) |> Map.put("context", context)
|> Map.put("conversation", context)
end end
def fix_attachments(object) do def fix_attachments(object) do
...@@ -137,13 +177,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do ...@@ -137,13 +177,22 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def fix_content_map(object), do: object def fix_content_map(object), do: object
# disallow objects with bogus IDs
def handle_incoming(%{"id" => nil}), do: :error
def handle_incoming(%{"id" => ""}), do: :error
# length of https:// = 8, should validate better, but good enough for now.
def handle_incoming(%{"id" => id}) when not (is_binary(id) and length(id) > 8), do: :error
# TODO: validate those with a Ecto scheme # TODO: validate those with a Ecto scheme
# - tags # - tags
# - emoji # - emoji
def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data) def handle_incoming(%{"type" => "Create", "object" => %{"type" => objtype} = object} = data)
when objtype in ["Article", "Note"] do when objtype in ["Article", "Note", "Video"] do
actor = get_actor(data) actor = get_actor(data)
data = Map.put(data, "actor", actor)
data =
Map.put(data, "actor", actor)
|> fix_addressing
with nil <- Activity.get_create_activity_by_object_ap_id(object["id"]), with nil <- Activity.get_create_activity_by_object_ap_id(object["id"]),
%User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do %User{} = user <- User.get_or_fetch_by_ap_id(data["actor"]) do
......
...@@ -128,7 +128,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do ...@@ -128,7 +128,7 @@ defmodule Pleroma.Web.ActivityPub.Utils do
Inserts a full object if it is contained in an activity. Inserts a full object if it is contained in an activity.
""" """
def insert_full_object(%{"object" => %{"type" => type} = object_data}) def insert_full_object(%{"object" => %{"type" => type} = object_data})
when is_map(object_data) and type in ["Article", "Note"] do when is_map(object_data) and type in ["Article", "Note", "Video"] do
with {:ok, _} <- Object.create(object_data) do with {:ok, _} <- Object.create(object_data) do
:ok :ok
end end
...@@ -204,13 +204,17 @@ defmodule Pleroma.Web.ActivityPub.Utils do ...@@ -204,13 +204,17 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do def add_like_to_object(%Activity{data: %{"actor" => actor}}, object) do
with likes <- [actor | object.data["likes"] || []] |> Enum.uniq() do likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
with likes <- [actor | likes] |> Enum.uniq() do
update_likes_in_object(likes, object) update_likes_in_object(likes, object)
end end
end end
def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do def remove_like_from_object(%Activity{data: %{"actor" => actor}}, object) do
with likes <- (object.data["likes"] || []) |> List.delete(actor) do likes = if is_list(object.data["likes"]), do: object.data["likes"], else: []
with likes <- likes |> List.delete(actor) do
update_likes_in_object(likes, object) update_likes_in_object(likes, object)
end end
end end
...@@ -357,13 +361,19 @@ defmodule Pleroma.Web.ActivityPub.Utils do ...@@ -357,13 +361,19 @@ defmodule Pleroma.Web.ActivityPub.Utils do
end end
def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do def add_announce_to_object(%Activity{data: %{"actor" => actor}}, object) do
with announcements <- [actor | object.data["announcements"] || []] |> Enum.uniq() do announcements =
if is_list(object.data["announcements"]), do: object.data["announcements"], else: []
with announcements <- [actor | announcements] |> Enum.uniq() do
update_element_in_object("announcement", announcements, object) update_element_in_object("announcement", announcements, object)
end end
end end
def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do def remove_announce_from_object(%Activity{data: %{"actor" => actor}}, object) do
with announcements <- (object.data["announcements"] || []) |> List.delete(actor) do announcements =
if is_list(object.data["announcements"]), do: object.data["announcements"], else: []
with announcements <- announcements |> List.delete(actor) do
update_element_in_object("announcement", announcements, object) update_element_in_object("announcement", announcements, object)
end end
end end
......
...@@ -98,9 +98,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do ...@@ -98,9 +98,6 @@ defmodule Pleroma.Web.ActivityPub.UserView do
info = User.user_info(user) info = User.user_info(user)
params = %{ params = %{
"type" => ["Create", "Announce"],
"actor_id" => user.ap_id,
"whole_db" => true,
"limit" => "10" "limit" => "10"
} }
...@@ -111,10 +108,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do ...@@ -111,10 +108,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
params params
end end
activities = ActivityPub.fetch_public_activities(params) activities = ActivityPub.fetch_user_activities(user, nil, params)
min_id = Enum.at(activities, 0).id min_id = Enum.at(Enum.reverse(activities), 0).id
activities = Enum.reverse(activities)
max_id = Enum.at(activities, 0).id max_id = Enum.at(activities, 0).id
collection = collection =
......
...@@ -64,7 +64,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do ...@@ -64,7 +64,6 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def make_content_html(status, mentions, attachments, tags, no_attachment_links \\ false) do def make_content_html(status, mentions, attachments, tags, no_attachment_links \\ false) do
status status
|> String.replace("\r", "")
|> format_input(mentions, tags) |> format_input(mentions, tags)
|> maybe_add_attachments(attachments, no_attachment_links) |> maybe_add_attachments(attachments, no_attachment_links)
end end
...@@ -95,7 +94,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do ...@@ -95,7 +94,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
def format_input(text, mentions, tags) do def format_input(text, mentions, tags) do
text text
|> Formatter.html_escape() |> Formatter.html_escape()
|> String.replace("\n", "<br>") |> String.replace(~r/\r?\n/, "<br>")
|> (&{[], &1}).() |> (&{[], &1}).()
|> Formatter.add_links() |> Formatter.add_links()
|> Formatter.add_user_links(mentions) |> Formatter.add_user_links(mentions)
...@@ -109,7 +108,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do ...@@ -109,7 +108,7 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> Enum.sort_by(fn {tag, _} -> -String.length(tag) end) |> Enum.sort_by(fn {tag, _} -> -String.length(tag) end)
Enum.reduce(tags, text, fn {full, tag}, text -> Enum.reduce(tags, text, fn {full, tag}, text ->
url = "#<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag}</a>" url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>##{tag}</a>"
String.replace(text, full, url) String.replace(text, full, url)
end) end)
end end
......
...@@ -5,7 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ...@@ -5,7 +5,7 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView} alias Pleroma.Web.MastodonAPI.{StatusView, AccountView, MastodonView, ListView}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.{CommonAPI, OStatus} alias Pleroma.Web.CommonAPI
alias Pleroma.Web.OAuth.{Authorization, Token, App} alias Pleroma.Web.OAuth.{Authorization, Token, App}
alias Comeonin.Pbkdf2 alias Comeonin.Pbkdf2
import Ecto.Query import Ecto.Query
...@@ -13,15 +13,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ...@@ -13,15 +13,20 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
@desc_max_nchars Application.get_env(:pleroma, :instance) @desc_max_nchars Application.get_env(:pleroma, :instance)
|> Keyword.get(:image_description_limit) |> Keyword.get(:image_description_limit)
@httpoison Application.get_env(:pleroma, :httpoison)
action_fallback(:errors) action_fallback(:errors)
def create_app(conn, params) do def create_app(conn, params) do
with cs <- App.register_changeset(%App{}, params) |> IO.inspect(), with cs <- App.register_changeset(%App{}, params) |> IO.inspect(),
{:ok, app} <- Repo.insert(cs) |> IO.inspect() do {:ok, app} <- Repo.insert(cs) |> IO.inspect() do
res = %{ res = %{
id: app.id, id: app.id |> to_string,
name: app.client_name,
client_id: app.client_id, client_id: app.client_id,
client_secret: app.client_secret client_secret: app.client_secret,
redirect_uri: app.redirect_uris,
website: app.website
} }
json(conn, res) json(conn, res)
...@@ -730,12 +735,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ...@@ -730,12 +735,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
fetched = fetched =
if Regex.match?(~r/https?:/, query) do if Regex.match?(~r/https?:/, query) do
with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do with {:ok, object} <- ActivityPub.fetch_object_from_id(query) do
activities [Activity.get_create_activity_by_object_ap_id(object.data["id"])]
|> Enum.filter(fn
%{data: %{"type" => "Create"}} -> true
_ -> false
end)
else else
_e -> [] _e -> []
end end
...@@ -782,12 +783,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ...@@ -782,12 +783,8 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
fetched = fetched =
if Regex.match?(~r/https?:/, query) do if Regex.match?(~r/https?:/, query) do
with {:ok, activities} <- OStatus.fetch_activity_from_url(query) do with {:ok, object} <- ActivityPub.fetch_object_from_id(query) do
activities [Activity.get_create_activity_by_object_ap_id(object.data["id"])]
|> Enum.filter(fn
%{data: %{"type" => "Create"}} -> true
_ -> false
end)
else else
_e -> [] _e -> []
end end
...@@ -1174,4 +1171,45 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do ...@@ -1174,4 +1171,45 @@ defmodule Pleroma.Web.MastodonAPI.MastodonAPIController do
|> put_status(500) |> put_status(500)
|> json("Something went wrong") |> json("Something went wrong")
end end
@suggestions Application.get_env(:pleroma, :suggestions)
def suggestions(%{assigns: %{user: user}} = conn, _) do
if Keyword.get(@suggestions, :enabled, false) do
api = Keyword.get(@suggestions, :third_party_engine, "")
timeout = Keyword.get(@suggestions, :timeout, 5000)
host =
Application.get_env(:pleroma, Pleroma.Web.Endpoint)
|> Keyword.get(:url)
|> Keyword.get(:host)
user = user.nickname
url = String.replace(api, "{{host}}", host) |> String.replace("{{user}}", user)
with {:ok, %{status_code: 200, body: body}} <-
@httpoison.get(url, [], timeout: timeout, recv_timeout: timeout),
{:ok, data} <- Jason.decode(body) do
data2 =
Enum.slice(data, 0, 40)
|> Enum.map(fn x ->
Map.put(
x,
"id",
case User.get_or_fetch(x["acct"]) do
%{id: id} -> id
_ -> 0
end
)
end)
conn
|> json(data2)
else
e -> Logger.error("Could not retrieve suggestions at fetch #{url}, #{inspect(e)}")
end
else
json(conn, [])
end
end
end end
...@@ -36,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do ...@@ -36,7 +36,7 @@ defmodule Pleroma.Web.MastodonAPI.AccountView do
followers_count: user_info.follower_count, followers_count: user_info.follower_count,
following_count: user_info.following_count, following_count: user_info.following_count,
statuses_count: user_info.note_count, statuses_count: user_info.note_count,
note: user.bio || "", note: HtmlSanitizeEx.basic_html(user.bio) || "",
url: user.ap_id, url: user.ap_id,
avatar: image, avatar: image,
avatar_static: image, avatar_static: image,
......
...@@ -99,8 +99,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do ...@@ -99,8 +99,9 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || []) repeated = opts[:for] && opts[:for].ap_id in (object["announcements"] || [])
favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || []) favorited = opts[:for] && opts[:for].ap_id in (object["likes"] || [])
attachments = attachment_data = object["attachment"] || []
render_many(object["attachment"] || [], StatusView, "attachment.json", as: :attachment) attachment_data = attachment_data ++ if object["type"] == "Video", do: [object], else: []
attachments = render_many(attachment_data, StatusView, "attachment.json", as: :attachment)
created_at = Utils.to_masto_date(object["published"]) created_at = Utils.to_masto_date(object["published"])
...@@ -176,12 +177,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do ...@@ -176,12 +177,16 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end end
def render("attachment.json", %{attachment: attachment}) do def render("attachment.json", %{attachment: attachment}) do
if is_list(attachment["url"]) do # if is_list(attachment["url"]) do
[%{"mediaType" => media_type, "href" => href} | _] = attachment["url"] # [%{"mediaType" => media_type, "href" => href} | _] = attachment["url"]
else # else
#WV This is more like Mastodon's ActivityPub format # #WV This is more like Mastodon's ActivityPub format
%{"mediaType" => media_type, "url" => href} = attachment # %{"mediaType" => media_type, "url" => href} = attachment
end # end
[attachment_url | _] = attachment["url"]
media_type = attachment_url["mediaType"] || attachment_url["mimeType"]
href = attachment_url["href"]
type = type =
cond do cond do
String.contains?(media_type, "image") -> "image" String.contains?(media_type, "image") -> "image"
...@@ -237,6 +242,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do ...@@ -237,6 +242,19 @@ defmodule Pleroma.Web.MastodonAPI.StatusView do
end end
end end
def render_content(%{"type" => "Video"} = object) do
name = object["name"]
content =
if !!name and name != "" do
"<p><a href=\"#{object["id"]}\">#{name}</a></p>#{object["content"]}"
else
object["content"]
end
HtmlSanitizeEx.basic_html(content)
end
def render_content(%{"type" => "Article"} = object) do def render_content(%{"type" => "Article"} = object) do
summary = object["name"] summary = object["name"]
......
...@@ -21,6 +21,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do ...@@ -21,6 +21,7 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
def nodeinfo(conn, %{"version" => "2.0"}) do def nodeinfo(conn, %{"version" => "2.0"}) do
instance = Application.get_env(:pleroma, :instance) instance = Application.get_env(:pleroma, :instance)
media_proxy = Application.get_env(:pleroma, :media_proxy) media_proxy = Application.get_env(:pleroma, :media_proxy)
suggestions = Application.get_env(:pleroma, :suggestions)
stats = Stats.get_stats() stats = Stats.get_stats()
response = %{ response = %{
...@@ -45,7 +46,13 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do ...@@ -45,7 +46,13 @@ defmodule Pleroma.Web.Nodeinfo.NodeinfoController do
nodeName: Keyword.get(instance, :name), nodeName: Keyword.get(instance, :name),
nodeDescription: Keyword.get(instance, :description), nodeDescription: Keyword.get(instance, :description),
mediaProxy: Keyword.get(media_proxy, :enabled), mediaProxy: Keyword.get(media_proxy, :enabled),
private: !Keyword.get(instance, :public, true) private: !Keyword.get(instance, :public, true),
suggestions: %{
enabled: Keyword.get(suggestions, :enabled, false),
thirdPartyEngine: Keyword.get(suggestions, :third_party_engine, ""),
timeout: Keyword.get(suggestions, :timeout, 5000),
web: Keyword.get(suggestions, :web, "")
}
} }
} }
......
...@@ -143,6 +143,8 @@ defmodule Pleroma.Web.Router do ...@@ -143,6 +143,8 @@ defmodule Pleroma.Web.Router do
get("/domain_blocks", MastodonAPIController, :domain_blocks) get("/domain_blocks", MastodonAPIController, :domain_blocks)
post("/domain_blocks", MastodonAPIController, :block_domain) post("/domain_blocks", MastodonAPIController, :block_domain)
delete("/domain_blocks", MastodonAPIController, :unblock_domain) delete("/domain_blocks", MastodonAPIController, :unblock_domain)
get("/suggestions", MastodonAPIController, :suggestions)
end end
scope "/api/web", Pleroma.Web.MastodonAPI do scope "/api/web", Pleroma.Web.MastodonAPI do
......
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
<script id='initial-state' type='application/json'><%= raw @initial_state %></script> <script id='initial-state' type='application/json'><%= raw @initial_state %></script>
<script src="/packs/application.js"></script> <script src="/packs/application.js"></script>
</head> </head>
<body class='app-body no-reduce-motion'> <body class='app-body no-reduce-motion system-font'>
<div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'> <div class='app-holder' data-props='{&quot;locale&quot;:&quot;en&quot;}' id='mastodon'>
</div> </div>
</body> </body>
......
...@@ -175,7 +175,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do ...@@ -175,7 +175,9 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
showWhoToFollowPanel: Keyword.get(@instance_fe, :show_who_to_follow_panel), showWhoToFollowPanel: Keyword.get(@instance_fe, :show_who_to_follow_panel),
scopeOptionsEnabled: Keyword.get(@instance_fe, :scope_options_enabled), scopeOptionsEnabled: Keyword.get(@instance_fe, :scope_options_enabled),
whoToFollowProvider: Keyword.get(@instance_fe, :who_to_follow_provider), whoToFollowProvider: Keyword.get(@instance_fe, :who_to_follow_provider),
whoToFollowLink: Keyword.get(@instance_fe, :who_to_follow_link) whoToFollowLink: Keyword.get(@instance_fe, :who_to_follow_link),
collapseMessageWithSubject:
Keyword.get(@instance_fe, :collapse_message_with_subject)
} }
} }
}) })
......
...@@ -170,6 +170,15 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do ...@@ -170,6 +170,15 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
HtmlSanitizeEx.basic_html(content) HtmlSanitizeEx.basic_html(content)
|> Formatter.emojify(object["emoji"]) |> Formatter.emojify(object["emoji"])
video =
if object["type"] == "Video" do
vid = [object]
else
[]
end
attachments = (object["attachment"] || []) ++ video
%{ %{
"id" => activity.id, "id" => activity.id,
"uri" => activity.data["object"]["id"], "uri" => activity.data["object"]["id"],
...@@ -181,7 +190,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do ...@@ -181,7 +190,7 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter do
"created_at" => created_at, "created_at" => created_at,
"in_reply_to_status_id" => object["inReplyToStatusId"], "in_reply_to_status_id" => object["inReplyToStatusId"],
"statusnet_conversation_id" => conversation_id, "statusnet_conversation_id" => conversation_id,
"attachments" => (object["attachment"] || []) |> ObjectRepresenter.enum_to_list(opts), "attachments" => attachments |> ObjectRepresenter.enum_to_list(opts),
"attentions" => attentions, "attentions" => attentions,
"fave_num" => like_count, "fave_num" => like_count,
"repeat_num" => announcement_count, "repeat_num" => announcement_count,
......
...@@ -7,18 +7,20 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do ...@@ -7,18 +7,20 @@ defmodule Pleroma.Web.TwitterAPI.Representers.ObjectRepresenter do
%{ %{
url: url["href"] |> Pleroma.Web.MediaProxy.url(), url: url["href"] |> Pleroma.Web.MediaProxy.url(),
mimetype: url["mediaType"], mimetype: url["mediaType"] || url["mimeType"],
id: data["uuid"], id: data["uuid"],
oembed: false oembed: false,
description: data["name"]
} }
end end
def to_map(%Object{data: %{"url" => url} = data}, _opts) when is_binary(url) do def to_map(%Object{data: %{"url" => url} = data}, _opts) when is_binary(url) do
%{ %{
url: url |> Pleroma.Web.MediaProxy.url(), url: url |> Pleroma.Web.MediaProxy.url(),
mimetype: data["mediaType"], mimetype: data["mediaType"] || url["mimeType"],
id: data["uuid"], id: data["uuid"],
oembed: false oembed: false,
description: data["name"]
} }
end end
......
...@@ -134,9 +134,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do ...@@ -134,9 +134,10 @@ defmodule Pleroma.Web.TwitterAPI.TwitterAPI do
} }
# no need to query DB if registration is open # no need to query DB if registration is open
unless @registrations_open || is_nil(tokenString) do token =
token = Repo.get_by(UserInviteToken, %{token: tokenString}) unless @registrations_open || is_nil(tokenString) do
end Repo.get_by(UserInviteToken, %{token: tokenString})
end
cond do cond do
@registrations_open || (!is_nil(token) && !token.used) -> @registrations_open || (!is_nil(token) && !token.used) ->
......
defmodule Pleroma.Web.TwitterAPI.Controller do defmodule Pleroma.Web.TwitterAPI.Controller do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Formatter
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView} alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.CommonAPI.Utils, as: CommonUtils
alias Pleroma.{Repo, Activity, User, Notification} alias Pleroma.{Repo, Activity, User, Notification}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.ActivityPub.Utils alias Pleroma.Web.ActivityPub.Utils
...@@ -413,8 +415,18 @@ defmodule Pleroma.Web.TwitterAPI.Controller do ...@@ -413,8 +415,18 @@ defmodule Pleroma.Web.TwitterAPI.Controller 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
bio_brs = Regex.replace(~r/\r?\n/, bio, "<br>") mentions = Formatter.parse_mentions(bio)
Map.put(params, "bio", bio_brs) tags = Formatter.parse_tags(bio)
emoji =
(user.info["source_data"]["tag"] || [])
|> Enum.filter(fn %{"type" => t} -> t == "Emoji" end)
|> Enum.map(fn %{"icon" => %{"url" => url}, "name" => name} ->
{String.trim(name, ":"), url}
end)
bio_html = CommonUtils.format_input(bio, mentions, tags)
Map.put(params, "bio", bio_html |> Formatter.emojify(emoji))
else else
params params
end end
......
...@@ -36,12 +36,11 @@ defmodule Pleroma.Web.TwitterAPI.UserView do ...@@ -36,12 +36,11 @@ defmodule Pleroma.Web.TwitterAPI.UserView do
{String.trim(name, ":"), url} {String.trim(name, ":"), url}
end) end)
bio = HtmlSanitizeEx.strip_tags(user.bio)
data = %{ data = %{
"created_at" => user.inserted_at |> Utils.format_naive_asctime(), "created_at" => user.inserted_at |> Utils.format_naive_asctime(),
"description" => bio, "description" =>
"description_html" => bio |> Formatter.emojify(emoji), HtmlSanitizeEx.strip_tags((user.bio || "") |> String.replace("<br>", "\n")),
"description_html" => HtmlSanitizeEx.basic_html(user.bio),
"favourites_count" => 0, "favourites_count" => 0,
"followers_count" => user_info[:follower_count], "followers_count" => user_info[:follower_count],
"following" => following, "following" => following,
......
...@@ -30,25 +30,25 @@ defmodule Pleroma.Mixfile do ...@@ -30,25 +30,25 @@ defmodule Pleroma.Mixfile do
# Type `mix help deps` for examples and options. # Type `mix help deps` for examples and options.
defp deps do defp deps do
[ [
{:phoenix, "~> 1.3.0"}, {:phoenix, "~> 1.3.3"},
{:phoenix_pubsub, "~> 1.0"}, {:phoenix_pubsub, "~> 1.0.2"},
{:phoenix_ecto, "~> 3.2"}, {:phoenix_ecto, "~> 3.3"},
{:postgrex, ">= 0.0.0"}, {:postgrex, ">= 0.13.5"},
{:gettext, "~> 0.11"}, {:gettext, "~> 0.15"},
{:cowboy, "~> 1.0", override: true}, {:cowboy, "~> 1.1.2", override: true},
{:comeonin, "~> 4.0"}, {:comeonin, "~> 4.1.1"},
{:pbkdf2_elixir, "~> 0.12"}, {:pbkdf2_elixir, "~> 0.12.3"},
{:trailing_format_plug, "~> 0.0.5"}, {:trailing_format_plug, "~> 0.0.7"},
{:html_sanitize_ex, "~> 1.3.0-rc1"}, {:html_sanitize_ex, "~> 1.3.0"},
{:phoenix_html, "~> 2.10"}, {:phoenix_html, "~> 2.10"},
{:calendar, "~> 0.16.1"}, {:calendar, "~> 0.17.4"},
{:cachex, "~> 3.0"}, {:cachex, "~> 3.0.2"},
{:httpoison, "~> 1.1.0"}, {:httpoison, "~> 1.2.0"},
{:jason, "~> 1.0"}, {:jason, "~> 1.0"},
{:ex_machina, "~> 2.0", only: :test}, {:mogrify, "~> 0.6.1"},
{:credo, "~> 0.7", only: [:dev, :test]}, {:ex_machina, "~> 2.2", only: :test},
{:mock, "~> 0.3.0", only: :test}, {:credo, "~> 0.9.3", only: [:dev, :test]},
{:mogrify, "~> 0.6.1"} {:mock, "~> 0.3.1", only: :test}
] ]
end end
......
This diff is collapsed.
<!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.5d0189b6f119febde070b703869bbd06.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.f2341edd686e54ee9b4a.js></script><script type=text/javascript src=/static/js/vendor.a93310d51acbd9480094.js></script><script type=text/javascript src=/static/js/app.de965bb2a0a8bffbeafa.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.e07fc4f73aee0cb0cbffee2eba536027.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.91829bef2424fbfc7631.js></script><script type=text/javascript src=/static/js/vendor.d590300dfdab65a9b597.js></script><script type=text/javascript src=/static/js/app.0a721b31076fdf8dbe6f.js></script></body></html>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
webpackJsonp([85],{676:function(e,c,t){"use strict";function o(e){var c=e.detail,t=c[0],o=document.querySelector('[data-id="'+t.id+'"]');o&&o.parentNode.removeChild(o)}Object.defineProperty(c,"__esModule",{value:!0});var n=t(150);t.n(n);[].forEach.call(document.querySelectorAll(".trash-button"),function(e){e.addEventListener("ajax:success",o)});var l='.batch-checkbox input[type="checkbox"]';Object(n.delegate)(document,"#batch_checkbox_all","change",function(e){var c=e.target;[].forEach.call(document.querySelectorAll(l),function(e){e.checked=c.checked})}),Object(n.delegate)(document,l,"change",function(){var e=document.querySelector("#batch_checkbox_all");e&&(e.checked=[].every.call(document.querySelectorAll(l),function(e){return e.checked}),e.indeterminate=!e.checked&&[].some.call(document.querySelectorAll(l),function(e){return e.checked}))}),Object(n.delegate)(document,".media-spoiler-show-button","click",function(){[].forEach.call(document.querySelectorAll("button.media-spoiler"),function(e){e.click()})}),Object(n.delegate)(document,".media-spoiler-hide-button","click",function(){[].forEach.call(document.querySelectorAll(".spoiler-button.spoiler-button--visible button"),function(e){e.click()})})}},[676]); webpackJsonp([85],{661:function(e,c,t){"use strict";function o(e){var c=e.detail,t=c[0],o=document.querySelector('[data-id="'+t.id+'"]');o&&o.parentNode.removeChild(o)}Object.defineProperty(c,"__esModule",{value:!0});var n=t(152);t.n(n);[].forEach.call(document.querySelectorAll(".trash-button"),function(e){e.addEventListener("ajax:success",o)});var l='.batch-checkbox input[type="checkbox"]';Object(n.delegate)(document,"#batch_checkbox_all","change",function(e){var c=e.target;[].forEach.call(document.querySelectorAll(l),function(e){e.checked=c.checked})}),Object(n.delegate)(document,l,"change",function(){var e=document.querySelector("#batch_checkbox_all");e&&(e.checked=[].every.call(document.querySelectorAll(l),function(e){return e.checked}),e.indeterminate=!e.checked&&[].some.call(document.querySelectorAll(l),function(e){return e.checked}))}),Object(n.delegate)(document,".media-spoiler-show-button","click",function(){[].forEach.call(document.querySelectorAll("button.media-spoiler"),function(e){e.click()})}),Object(n.delegate)(document,".media-spoiler-hide-button","click",function(){[].forEach.call(document.querySelectorAll(".spoiler-button.spoiler-button--visible button"),function(e){e.click()})})}},[661]);
//# sourceMappingURL=admin.js.map //# sourceMappingURL=admin.js.map
\ No newline at end of file
This diff is collapsed.
CACHE MANIFEST CACHE MANIFEST
#ver:2018-6-3 17:25:50 #ver:2018-8-12 18:01:32
#plugin:4.8.4 #plugin:4.8.4
CACHE: CACHE:
...@@ -16,21 +16,21 @@ CACHE: ...@@ -16,21 +16,21 @@ CACHE:
/packs/features/direct_timeline.js /packs/features/direct_timeline.js
/packs/features/pinned_statuses.js /packs/features/pinned_statuses.js
/packs/features/domain_blocks.js /packs/features/domain_blocks.js
/packs/features/favourited_statuses.js
/packs/features/list_timeline.js
/packs/features/following.js /packs/features/following.js
/packs/features/followers.js /packs/features/followers.js
/packs/features/hashtag_timeline.js /packs/features/favourited_statuses.js
/packs/features/list_timeline.js
/packs/features/account_gallery.js /packs/features/account_gallery.js
/packs/features/hashtag_timeline.js
/packs/features/status.js /packs/features/status.js
/packs/features/lists.js /packs/features/lists.js
/packs/modals/report_modal.js /packs/modals/report_modal.js
/packs/features/getting_started.js
/packs/features/follow_requests.js /packs/features/follow_requests.js
/packs/features/mutes.js /packs/features/mutes.js
/packs/features/blocks.js /packs/features/blocks.js
/packs/features/reblogs.js /packs/features/reblogs.js
/packs/features/favourites.js /packs/features/favourites.js
/packs/features/getting_started.js
/packs/features/keyboard_shortcuts.js /packs/features/keyboard_shortcuts.js
/packs/modals/mute_modal.js /packs/modals/mute_modal.js
/packs/features/generic_not_found.js /packs/features/generic_not_found.js
......
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
{"version":3,"sources":[],"names":[],"mappings":"","file":"contrast.css","sourceRoot":""}
\ No newline at end of file
webpackJsonp([82],{803:function(n,c){}},[803]);
//# sourceMappingURL=contrast.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///contrast.js"],"names":["webpackJsonp","803","module","exports"],"mappings":"AAAAA,cAAc,KAERC,IACA,SAAUC,EAAQC,OAMrB","file":"contrast.js","sourcesContent":["webpackJsonp([82],{\n\n/***/ 803:\n/***/ (function(module, exports) {\n\n// removed by extract-text-webpack-plugin\n\n/***/ })\n\n},[803]);\n\n\n// WEBPACK FOOTER //\n// contrast.js"],"sourceRoot":""}
\ No newline at end of file
This diff is collapsed.
webpackJsonp([83],{817:function(n,c){}},[817]); webpackJsonp([83],{802:function(n,c){}},[802]);
//# sourceMappingURL=default.js.map //# sourceMappingURL=default.js.map
\ No newline at end of file
{"version":3,"sources":["webpack:///default.js"],"names":["webpackJsonp","817","module","exports"],"mappings":"AAAAA,cAAc,KAERC,IACA,SAAUC,EAAQC,OAMrB","file":"default.js","sourcesContent":["webpackJsonp([83],{\n\n/***/ 817:\n/***/ (function(module, exports) {\n\n// removed by extract-text-webpack-plugin\n\n/***/ })\n\n},[817]);\n\n\n// WEBPACK FOOTER //\n// default.js"],"sourceRoot":""} {"version":3,"sources":["webpack:///default.js"],"names":["webpackJsonp","802","module","exports"],"mappings":"AAAAA,cAAc,KAERC,IACA,SAAUC,EAAQC,OAMrB","file":"default.js","sourcesContent":["webpackJsonp([83],{\n\n/***/ 802:\n/***/ (function(module, exports) {\n\n// removed by extract-text-webpack-plugin\n\n/***/ })\n\n},[802]);\n\n\n// WEBPACK FOOTER //\n// default.js"],"sourceRoot":""}
\ No newline at end of file \ No newline at end of file