Merge branch 'develop' of ssh.gitgud.io:lambadalambda/pleroma into feature/user-timeline

This commit is contained in:
dtluna 2017-04-16 17:08:17 +03:00
commit 5229b01944
22 changed files with 143 additions and 47 deletions

View file

@ -18,6 +18,31 @@ def store(%Plug.Upload{} = file) do
} }
end end
def store(%{"img" => "data:image/" <> image_data}) do
parsed = Regex.named_captures(~r/(?<filetype>jpeg|png|gif);base64,(?<data>.*)/, image_data)
data = Base.decode64!(parsed["data"])
uuid = Ecto.UUID.generate
upload_folder = Path.join(upload_path(), uuid)
File.mkdir_p!(upload_folder)
filename = Base.encode16(:crypto.hash(:sha256, data)) <> ".#{parsed["filetype"]}"
result_file = Path.join(upload_folder, filename)
File.write!(result_file, data)
content_type = "image/#{parsed["filetype"]}"
%{
"type" => "Image",
"url" => [%{
"type" => "Link",
"mediaType" => content_type,
"href" => url_for(Path.join(uuid, filename))
}],
"name" => filename,
"uuid" => uuid
}
end
defp upload_path do defp upload_path do
Application.get_env(:pleroma, Pleroma.Upload) Application.get_env(:pleroma, Pleroma.Upload)
|> Keyword.fetch!(:uploads) |> Keyword.fetch!(:uploads)

View file

@ -13,6 +13,7 @@ defmodule Pleroma.User do
field :password_confirmation, :string, virtual: true field :password_confirmation, :string, virtual: true
field :following, { :array, :string }, default: [] field :following, { :array, :string }, default: []
field :ap_id, :string field :ap_id, :string
field :avatar, :map
timestamps() timestamps()
end end

View file

@ -174,7 +174,7 @@ def fetch_activities_for_context(context) do
Repo.all(query) Repo.all(query)
end end
def upload(%Plug.Upload{} = file) do def upload(file) do
data = Upload.store(file) data = Upload.store(file)
Repo.insert(%Object{data: data}) Repo.insert(%Object{data: data})
end end

View file

@ -46,5 +46,6 @@ def user_fetcher(username) do
post "/favorites/create", TwitterAPI.Controller, :favorite post "/favorites/create", TwitterAPI.Controller, :favorite
post "/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite post "/favorites/destroy/:id", TwitterAPI.Controller, :unfavorite
post "/statuses/retweet/:id", TwitterAPI.Controller, :retweet post "/statuses/retweet/:id", TwitterAPI.Controller, :retweet
post "/qvitter/update_avatar", TwitterAPI.Controller, :update_avatar
end end
end end

View file

@ -4,8 +4,10 @@ defmodule Pleroma.Web.TwitterAPI.Representers.UserRepresenter do
alias Pleroma.User alias Pleroma.User
def to_map(user, opts) do def to_map(user, opts) do
image = case user.avatar do
image = "https://placehold.it/48x48" %{"url" => [%{"href" => href} | _]} -> href
_ -> "https://placehold.it/48x48"
end
following = if opts[:for] do following = if opts[:for] do
User.following?(opts[:for], user) User.following?(opts[:for], user)

View file

@ -3,6 +3,7 @@ defmodule Pleroma.Web.TwitterAPI.Controller do
alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ActivityRepresenter} alias Pleroma.Web.TwitterAPI.Representers.{UserRepresenter, ActivityRepresenter}
alias Pleroma.{Repo, Activity} alias Pleroma.{Repo, Activity}
alias Pleroma.Web.ActivityPub.ActivityPub
def verify_credentials(%{assigns: %{user: user}} = conn, _params) do def verify_credentials(%{assigns: %{user: user}} = conn, _params) do
response = user |> UserRepresenter.to_json(%{for: user}) response = user |> UserRepresenter.to_json(%{for: user})
@ -154,6 +155,18 @@ def register(conn, params) do
end end
end end
def update_avatar(%{assigns: %{user: user}} = conn, params) do
{:ok, object} = ActivityPub.upload(params)
change = Ecto.Changeset.change(user, %{avatar: object.data})
{:ok, user} = Repo.update(change)
response = UserRepresenter.to_map(user, %{for: user})
|> Poison.encode!
conn
|> json_reply(200, response)
end
defp bad_request_reply(conn, error_message) do defp bad_request_reply(conn, error_message) do
json = Poison.encode!(%{"error" => error_message}) json = Poison.encode!(%{"error" => error_message})
json_reply(conn, 400, json) json_reply(conn, 400, json)

View file

@ -0,0 +1,9 @@
defmodule Pleroma.Repo.Migrations.AddAvatarObjectToUsers do
use Ecto.Migration
def change do
alter table(:users) do
add :avatar, :map
end
end
end

View file

@ -1 +1 @@
<!DOCTYPE html><html><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.a86c13eb46180b1b975a1acd59b52cc6.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.da7ea91e505330123f38.js></script><script type=text/javascript src=/static/js/vendor.d7d8813599feb765b152.js></script><script type=text/javascript src=/static/js/app.dcc60205ebdef9eb3d87.js></script></body></html> <!DOCTYPE html><html><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.a86c13eb46180b1b975a1acd59b52cc6.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.66d994092e61600982a8.js></script><script type=text/javascript src=/static/js/vendor.d7d8813599feb765b152.js></script><script type=text/javascript src=/static/js/app.d4e0a640b375c4b52997.js></script></body></html>

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(n){if(r[n])return r[n].exports;var a=r[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n=window.webpackJsonp;window.webpackJsonp=function(o,c){for(var p,s,l=0,i=[];l<o.length;l++)s=o[l],a[s]&&i.push.apply(i,a[s]),a[s]=0;for(p in c)e[p]=c[p];for(n&&n(o,c);i.length;)i.shift().call(null,t);if(c[0])return r[0]=0,t(0)};var r={},a={0:0};t.e=function(e,n){if(0===a[e])return n.call(null,t);if(void 0!==a[e])a[e].push(n);else{a[e]=[n];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:"d7d8813599feb765b152",2:"d4e0a640b375c4b52997"}[e]+".js",r.appendChild(o)}},t.m=e,t.c=r,t.p="/"}([]);
//# sourceMappingURL=manifest.66d994092e61600982a8.js.map

View file

@ -1,2 +0,0 @@
!function(e){function t(n){if(r[n])return r[n].exports;var a=r[n]={exports:{},id:n,loaded:!1};return e[n].call(a.exports,a,a.exports,t),a.loaded=!0,a.exports}var n=window.webpackJsonp;window.webpackJsonp=function(c,o){for(var p,s,l=0,d=[];l<c.length;l++)s=c[l],a[s]&&d.push.apply(d,a[s]),a[s]=0;for(p in o)e[p]=o[p];for(n&&n(c,o);d.length;)d.shift().call(null,t);if(o[0])return r[0]=0,t(0)};var r={},a={0:0};t.e=function(e,n){if(0===a[e])return n.call(null,t);if(void 0!==a[e])a[e].push(n);else{a[e]=[n];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:"d7d8813599feb765b152",2:"dcc60205ebdef9eb3d87"}[e]+".js",r.appendChild(c)}},t.m=e,t.c=r,t.p="/"}([]);
//# sourceMappingURL=manifest.da7ea91e505330123f38.js.map

View file

@ -3,7 +3,7 @@ defmodule Pleroma.Builders.ActivityBuilder do
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
def build(data \\ %{}, opts \\ %{}) do def build(data \\ %{}, opts \\ %{}) do
user = opts[:user] || UserBuilder.build user = opts[:user] || Pleroma.Factory.insert(:user)
activity = %{ activity = %{
"id" => 1, "id" => 1,
"actor" => user.ap_id, "actor" => user.ap_id,
@ -29,7 +29,7 @@ def insert_list(times, data \\ %{}, opts \\ %{}) do
end end
def public_and_non_public do def public_and_non_public do
{:ok, user} = UserBuilder.insert user = Pleroma.Factory.insert(:user)
public = build(%{"id" => 1}, %{user: user}) public = build(%{"id" => 1}, %{user: user})
non_public = build(%{"id" => 2, "to" => []}, %{user: user}) non_public = build(%{"id" => 2, "to" => []}, %{user: user})

View file

@ -3,6 +3,8 @@ defmodule Pleroma.UserTest do
alias Pleroma.User alias Pleroma.User
use Pleroma.DataCase use Pleroma.DataCase
import Pleroma.Factory
test "ap_id returns the activity pub id for the user" do test "ap_id returns the activity pub id for the user" do
host = host =
Application.get_env(:pleroma, Pleroma.Web.Endpoint) Application.get_env(:pleroma, Pleroma.Web.Endpoint)
@ -25,21 +27,21 @@ test "ap_followers returns the followers collection for the user" do
end end
test "follow takes a user and another user" do test "follow takes a user and another user" do
{ :ok, user } = UserBuilder.insert user = insert(:user)
{ :ok, following } = UserBuilder.insert(%{nickname: "guy"}) followed = insert(:user)
{:ok, user } = User.follow(user, following) {:ok, user } = User.follow(user, followed)
user = Repo.get(User, user.id) user = Repo.get(User, user.id)
assert user.following == [User.ap_followers(following)] assert user.following == [User.ap_followers(followed)]
end end
test "unfollow takes a user and another user" do test "unfollow takes a user and another user" do
{ :ok, following } = UserBuilder.insert(%{nickname: "guy"}) followed = insert(:user)
{ :ok, user } = UserBuilder.insert(%{following: [User.ap_followers(following)]}) user = insert(:user, %{following: [User.ap_followers(followed)]})
{:ok, user } = User.unfollow(user, following) {:ok, user } = User.unfollow(user, followed)
user = Repo.get(User, user.id) user = Repo.get(User, user.id)
@ -47,8 +49,8 @@ test "unfollow takes a user and another user" do
end end
test "test if a user is following another user" do test "test if a user is following another user" do
{ :ok, followed } = UserBuilder.insert(%{nickname: "guy"}) followed = insert(:user)
{ :ok, user } = UserBuilder.insert(%{following: [User.ap_followers(followed)]}) user = insert(:user, %{following: [User.ap_followers(followed)]})
assert User.following?(user, followed) assert User.following?(user, followed)
refute User.following?(followed, user) refute User.following?(followed, user)

File diff suppressed because one or more lines are too long

View file

@ -45,8 +45,11 @@ test "a like activity" do
test "an activity" do test "an activity" do
{:ok, user} = UserBuilder.insert {:ok, user} = UserBuilder.insert
{:ok, mentioned_user } = UserBuilder.insert(%{nickname: "shp", ap_id: "shp"}) # {:ok, mentioned_user } = UserBuilder.insert(%{nickname: "shp", ap_id: "shp"})
{:ok, follower} = UserBuilder.insert(%{following: [User.ap_followers(user)]}) mentioned_user = insert(:user, %{nickname: "shp"})
# {:ok, follower} = UserBuilder.insert(%{following: [User.ap_followers(user)]})
follower = insert(:user, %{following: [User.ap_followers(user)]})
object = %Object{ object = %Object{
data: %{ data: %{
@ -62,7 +65,7 @@ test "an activity" do
} }
} }
content_html = "Some content mentioning <a href='shp'>@shp</shp>" content_html = "Some content mentioning <a href='#{mentioned_user.ap_id}'>@shp</shp>"
content = HtmlSanitizeEx.strip_tags(content_html) content = HtmlSanitizeEx.strip_tags(content_html)
date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601 date = DateTime.from_naive!(~N[2016-05-24 13:26:08.003], "Etc/UTC") |> DateTime.to_iso8601

View file

@ -5,13 +5,23 @@ defmodule Pleroma.Web.TwitterAPI.Representers.UserRepresenterTest do
alias Pleroma.Web.TwitterAPI.Representers.UserRepresenter alias Pleroma.Web.TwitterAPI.Representers.UserRepresenter
alias Pleroma.Builders.UserBuilder alias Pleroma.Builders.UserBuilder
import Pleroma.Factory
setup do setup do
{:ok, user} = UserBuilder.insert user = insert(:user)
[user: user] [user: user]
end end
test "A user with an avatar object", %{user: user} do
image = "image"
user = %{ user | avatar: %{ "url" => [%{"href" => image}] }}
represented = UserRepresenter.to_map(user)
assert represented["profile_image_url"] == image
end
test "A user", %{user: user} do test "A user", %{user: user} do
image = "https://placehold.it/48x48" image = "https://placehold.it/48x48"
represented = %{ represented = %{
"id" => user.id, "id" => user.id,
"name" => user.name, "name" => user.name,

View file

@ -94,10 +94,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
{:ok, user} = UserBuilder.insert user = insert(:user)
activities = ActivityBuilder.insert_list(30, %{"to" => [User.ap_followers(user)]}, %{user: user}) activities = ActivityBuilder.insert_list(30, %{"to" => [User.ap_followers(user)]}, %{user: user})
returned_activities = ActivityBuilder.insert_list(10, %{"to" => [User.ap_followers(user)]}, %{user: user}) returned_activities = ActivityBuilder.insert_list(10, %{"to" => [User.ap_followers(user)]}, %{user: user})
{:ok, other_user} = UserBuilder.insert(%{ap_id: "glimmung", nickname: "nockame"}) other_user = insert(:user)
ActivityBuilder.insert_list(10, %{}, %{user: other_user}) ActivityBuilder.insert_list(10, %{}, %{user: other_user})
since_id = List.last(activities).id since_id = List.last(activities).id
@ -110,7 +110,7 @@ test "with credentials", %{conn: conn, user: current_user} do
response = json_response(conn, 200) response = json_response(conn, 200)
assert length(response) == 10 assert length(response) == 10
assert response == Enum.map(returned_activities, fn (activity) -> ActivityRepresenter.to_map(activity, %{user: user, for: current_user}) end) assert response == Enum.map(returned_activities, fn (activity) -> ActivityRepresenter.to_map(activity, %{user: User.get_cached_by_ap_id(activity.data["actor"]), for: current_user}) end)
end end
end end
@ -122,7 +122,7 @@ 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
{:ok, followed } = UserBuilder.insert(%{name: "some guy"}) followed = insert(:user)
conn = conn conn = conn
|> with_credentials(current_user.nickname, "test") |> with_credentials(current_user.nickname, "test")
@ -142,7 +142,7 @@ 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
{:ok, followed } = UserBuilder.insert(%{name: "some guy"}) followed = insert(:user)
{:ok, current_user} = User.follow(current_user, followed) {:ok, current_user} = User.follow(current_user, followed)
assert current_user.following == [User.ap_followers(followed)] assert current_user.following == [User.ap_followers(followed)]
@ -157,6 +157,24 @@ test "with credentials", %{conn: conn, user: current_user} do
end end
end end
describe "POST /api/qvitter/update_avatar.json" do
setup [:valid_user]
test "without valid credentials", %{conn: conn} do
conn = post conn, "/api/qvitter/update_avatar.json"
assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
end
test "with credentials", %{conn: conn, user: current_user} do
conn = conn
|> with_credentials(current_user.nickname, "test")
|> post("/api/qvitter/update_avatar.json", %{img: Pleroma.Web.ActivityPub.ActivityPubTest.data_uri})
current_user = Repo.get(User, current_user.id)
assert is_map(current_user.avatar)
assert json_response(conn, 200) == UserRepresenter.to_map(current_user, %{for: current_user})
end
end
describe "POST /api/favorites/create/:id" do describe "POST /api/favorites/create/:id" do
setup [:valid_user] setup [:valid_user]
test "without valid credentials", %{conn: conn} do test "without valid credentials", %{conn: conn} do

View file

@ -78,7 +78,8 @@ test "create a status that is a reply" do
test "fetch public statuses" do test "fetch public statuses" do
%{ public: activity, user: user } = ActivityBuilder.public_and_non_public %{ public: activity, user: user } = ActivityBuilder.public_and_non_public
{:ok, follower } = UserBuilder.insert(%{name: "dude", ap_id: "idididid", following: [User.ap_followers(user)]})
follower = insert(:user, following: [User.ap_followers(user)])
statuses = TwitterAPI.fetch_public_statuses(follower) statuses = TwitterAPI.fetch_public_statuses(follower)
@ -87,19 +88,18 @@ test "fetch public statuses" do
end end
test "fetch friends' statuses" do test "fetch friends' statuses" do
ActivityBuilder.public_and_non_public user = insert(:user, %{following: ["someguy/followers"]})
{:ok, activity} = ActivityBuilder.insert(%{"to" => ["someguy/followers"]}) {:ok, activity} = ActivityBuilder.insert(%{"to" => ["someguy/followers"]})
{:ok, direct_activity} = ActivityBuilder.insert(%{"to" => ["some other id"]}) {:ok, direct_activity} = ActivityBuilder.insert(%{"to" => [user.ap_id]})
{:ok, user} = UserBuilder.insert(%{ap_id: "some other id", following: ["someguy/followers"]})
statuses = TwitterAPI.fetch_friend_statuses(user) statuses = TwitterAPI.fetch_friend_statuses(user)
activity_user = Repo.get_by(User, ap_id: activity.data["actor"]) activity_user = Repo.get_by(User, ap_id: activity.data["actor"])
direct_activity_user = Repo.get_by(User, ap_id: direct_activity.data["actor"])
assert length(statuses) == 2 assert length(statuses) == 2
assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: activity_user}) assert Enum.at(statuses, 0) == ActivityRepresenter.to_map(activity, %{user: activity_user})
assert Enum.at(statuses, 1) == ActivityRepresenter.to_map(direct_activity, %{user: activity_user, mentioned: [user]}) assert Enum.at(statuses, 1) == ActivityRepresenter.to_map(direct_activity, %{user: direct_activity_user, mentioned: [user]})
end end
test "get a user by params" do test "get a user by params" do
@ -145,8 +145,8 @@ test "fetch a single status" do
end end
test "Follow another user" do test "Follow another user" do
{ :ok, user } = UserBuilder.insert user = insert(:user)
{ :ok, following } = UserBuilder.insert(%{nickname: "guy"}) following = insert(:user)
{:ok, user, following, activity } = TwitterAPI.follow(user, following.id) {:ok, user, following, activity } = TwitterAPI.follow(user, following.id)
@ -158,8 +158,8 @@ test "Follow another user" do
end end
test "Unfollow another user" do test "Unfollow another user" do
{ :ok, following } = UserBuilder.insert(%{nickname: "guy"}) following = insert(:user)
{ :ok, user } = UserBuilder.insert(%{following: [User.ap_followers(following)]}) user = insert(:user, %{following: [User.ap_followers(following)]})
{:ok, user, _following } = TwitterAPI.unfollow(user, following.id) {:ok, user, _following } = TwitterAPI.unfollow(user, following.id)
@ -192,8 +192,8 @@ test "upload a file" do
test "it can parse mentions and return the relevant users" do test "it can parse mentions and return the relevant users" do
text = "@gsimg According to @archaeme , that is @daggsy." text = "@gsimg According to @archaeme , that is @daggsy."
{:ok, gsimg} = UserBuilder.insert(%{nickname: "gsimg"}) gsimg = insert(:user, %{nickname: "gsimg"})
{:ok, archaeme} = UserBuilder.insert(%{nickname: "archaeme"}) archaeme = insert(:user, %{nickname: "archaeme"})
expected_result = [ expected_result = [
{"@gsimg", gsimg}, {"@gsimg", gsimg},
@ -206,11 +206,11 @@ test "it can parse mentions and return the relevant users" do
test "it adds user links to an existing text" do test "it adds user links to an existing text" do
text = "@gsimg According to @archaeme , that is @daggsy." text = "@gsimg According to @archaeme , that is @daggsy."
{:ok, _gsimg} = UserBuilder.insert(%{nickname: "gsimg", ap_id: "first_link" }) gsimg = insert(:user, %{nickname: "gsimg"})
{:ok, _archaeme} = UserBuilder.insert(%{nickname: "archaeme", ap_id: "second_link"}) archaeme = insert(:user, %{nickname: "archaeme"})
mentions = TwitterAPI.parse_mentions(text) mentions = TwitterAPI.parse_mentions(text)
expected_text = "<a href='first_link'>@gsimg</a> According to <a href='second_link'>@archaeme</a> , that is @daggsy." expected_text = "<a href='#{gsimg.ap_id}'>@gsimg</a> According to <a href='#{archaeme.ap_id}'>@archaeme</a> , that is @daggsy."
assert TwitterAPI.add_user_links(text, mentions) == expected_text assert TwitterAPI.add_user_links(text, mentions) == expected_text
end end