Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into feature/unrepeats

This commit is contained in:
Francis Dinh 2018-04-20 16:47:44 -04:00
commit c5dc7e6e31
43 changed files with 330 additions and 39 deletions

6
.gitignore vendored
View File

@ -6,6 +6,9 @@
/uploads /uploads
/test/uploads /test/uploads
# Prevent committing custom emojis
/priv/static/emoji/custom/*
# Generated on crash by the VM # Generated on crash by the VM
erl_crash.dump erl_crash.dump
@ -16,3 +19,6 @@ erl_crash.dump
# secrets files as long as you replace their contents by environment # secrets files as long as you replace their contents by environment
# variables. # variables.
/config/*.secret.exs /config/*.secret.exs
# Database setup file, some may forget to delete it
/config/setup_db.psql

View File

@ -9,6 +9,7 @@ variables:
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
stages: stages:
- lint
- test - test
before_script: before_script:
@ -18,6 +19,11 @@ before_script:
- MIX_ENV=test mix ecto.create - MIX_ENV=test mix ecto.create
- MIX_ENV=test mix ecto.migrate - MIX_ENV=test mix ecto.migrate
lint:
stage: lint
script:
- MIX_ENV=test mix format --check-formatted
unit-testing: unit-testing:
stage: test stage: test
script: script:

View File

@ -9,20 +9,6 @@ def run(_) do
name = IO.gets("What is the name of your instance? (e.g. Pleroma/Soykaf): ") |> String.trim() name = IO.gets("What is the name of your instance? (e.g. Pleroma/Soykaf): ") |> String.trim()
email = IO.gets("What's your admin email address: ") |> String.trim() email = IO.gets("What's your admin email address: ") |> String.trim()
mediaproxy =
IO.gets("Do you want to activate the mediaproxy? (y/N): ")
|> String.trim()
|> String.downcase()
|> String.starts_with?("y")
proxy_url =
if mediaproxy do
IO.gets("What is the mediaproxy's URL? (e.g. https://cache.example.com): ")
|> String.trim()
else
"https://cache.example.com"
end
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
@ -35,8 +21,6 @@ def run(_) do
email: email, email: email,
name: name, name: name,
secret: secret, secret: secret,
mediaproxy: mediaproxy,
proxy_url: proxy_url,
dbpass: dbpass dbpass: dbpass
) )

View File

@ -1,14 +1,14 @@
defmodule Mix.Tasks.RmUser do defmodule Mix.Tasks.RmUser do
use Mix.Task use Mix.Task
import Mix.Ecto import Mix.Ecto
alias Pleroma.User alias Pleroma.{User, Repo}
@shortdoc "Permanently delete a user" @shortdoc "Permanently delete a user"
def run([nickname]) do def run([nickname]) do
ensure_started(Repo, []) Mix.Task.run("app.start")
with %User{local: true} = user <- User.get_by_nickname(nickname) do with %User{local: true} = user <- User.get_by_nickname(nickname) do
user.delete() User.delete(user)
end end
end end
end end

View File

@ -11,9 +11,9 @@ config :pleroma, :instance,
registrations_open: true registrations_open: true
config :pleroma, :media_proxy, config :pleroma, :media_proxy,
enabled: <%= mediaproxy %>, enabled: false,
redirect_on_failure: true, redirect_on_failure: true
base_url: "<%= proxy_url %>" #base_url: "https://cache.pleroma.social"
# Configure your database # Configure your database
config :pleroma, Pleroma.Repo, config :pleroma, Pleroma.Repo,

View File

@ -250,6 +250,13 @@ def get_by_nickname(nickname) do
Repo.get_by(User, nickname: nickname) Repo.get_by(User, nickname: nickname)
end end
def get_by_nickname_or_email(nickname_or_email) do
case user = Repo.get_by(User, nickname: nickname_or_email) do
%User{} -> user
nil -> Repo.get_by(User, email: nickname_or_email)
end
end
def get_cached_user_info(user) do def get_cached_user_info(user) do
key = "user_info:#{user.id}" key = "user_info:#{user.id}"
Cachex.get!(:user_cache, key, fallback: fn _ -> user_info(user) end) Cachex.get!(:user_cache, key, fallback: fn _ -> user_info(user) end)

View File

@ -61,6 +61,7 @@ def to_for_user_and_mentions(user, mentions, inReplyTo, "direct") 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

View File

@ -217,8 +217,8 @@ def user_statuses(%{assigns: %{user: user}} = conn, params) do
activities = [] activities = []
else else
activities = activities =
ActivityPub.fetch_public_activities(params) ActivityPub.fetch_public_activities(params)
|> Enum.reverse() |> Enum.reverse()
end end
conn conn
@ -700,7 +700,7 @@ defp get_or_make_app() do
end end
def login_post(conn, %{"authorization" => %{"name" => name, "password" => password}}) do def login_post(conn, %{"authorization" => %{"name" => name, "password" => password}}) do
with %User{} = user <- User.get_cached_by_nickname(name), with %User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash), true <- Pbkdf2.checkpw(password, user.password_hash),
{:ok, app} <- get_or_make_app(), {:ok, app} <- get_or_make_app(),
{:ok, auth} <- Authorization.create_authorization(app, user), {:ok, auth} <- Authorization.create_authorization(app, user),

View File

@ -29,7 +29,7 @@ def create_authorization(conn, %{
"redirect_uri" => redirect_uri "redirect_uri" => redirect_uri
} = params } = params
}) do }) do
with %User{} = user <- User.get_cached_by_nickname(name), with %User{} = user <- User.get_by_nickname_or_email(name),
true <- Pbkdf2.checkpw(password, user.password_hash), true <- Pbkdf2.checkpw(password, user.password_hash),
%App{} = app <- Repo.get_by(App, client_id: client_id), %App{} = app <- Repo.get_by(App, client_id: client_id),
{:ok, auth} <- Authorization.create_authorization(app, user) do {:ok, auth} <- Authorization.create_authorization(app, user) do

View File

@ -213,6 +213,7 @@ def user_fetcher(username) do
get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline) get("/statuses/friends_timeline", TwitterAPI.Controller, :friends_timeline)
get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline) get("/statuses/mentions", TwitterAPI.Controller, :mentions_timeline)
get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline) get("/statuses/mentions_timeline", TwitterAPI.Controller, :mentions_timeline)
get("/qvitter/statuses/notifications", TwitterAPI.Controller, :notifications)
post("/statuses/update", TwitterAPI.Controller, :status_update) post("/statuses/update", TwitterAPI.Controller, :status_update)
post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet) post("/statuses/retweet/:id", TwitterAPI.Controller, :retweet)

View File

@ -3,7 +3,7 @@
<h2><%= @error %></h2> <h2><%= @error %></h2>
<% end %> <% end %>
<%= form_for @conn, mastodon_api_path(@conn, :login), [as: "authorization"], fn f -> %> <%= form_for @conn, mastodon_api_path(@conn, :login), [as: "authorization"], fn f -> %>
<%= text_input f, :name, placeholder: "Username" %> <%= text_input f, :name, placeholder: "Username or email" %>
<br> <br>
<%= password_input f, :password, placeholder: "Password" %> <%= password_input f, :password, placeholder: "Password" %>
<br> <br>

View File

@ -2,7 +2,7 @@
<p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p> <p class="alert alert-danger" role="alert"><%= get_flash(@conn, :error) %></p>
<h2>OAuth Authorization</h2> <h2>OAuth Authorization</h2>
<%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %> <%= form_for @conn, o_auth_path(@conn, :authorize), [as: "authorization"], fn f -> %>
<%= label f, :name, "Name" %> <%= label f, :name, "Name or email" %>
<%= text_input f, :name %> <%= text_input f, :name %>
<br> <br>
<%= label f, :password, "Password" %> <%= label f, :password, "Password" %>

View File

@ -1,8 +1,8 @@
defmodule Pleroma.Web.TwitterAPI.Controller do defmodule Pleroma.Web.TwitterAPI.Controller do
use Pleroma.Web, :controller use Pleroma.Web, :controller
alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView} alias Pleroma.Web.TwitterAPI.{TwitterAPI, UserView, ActivityView, NotificationView}
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.{Repo, Activity, User} alias Pleroma.{Repo, Activity, User, Notification}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Ecto.Changeset alias Ecto.Changeset
@ -119,6 +119,13 @@ def mentions_timeline(%{assigns: %{user: user}} = conn, params) do
|> render(ActivityView, "index.json", %{activities: activities, for: user}) |> render(ActivityView, "index.json", %{activities: activities, for: user})
end end
def notifications(%{assigns: %{user: user}} = conn, params) do
notifications = Notification.for_user(user, params)
conn
|> render(NotificationView, "notification.json", %{notifications: notifications, for: user})
end
def follow(%{assigns: %{user: user}} = conn, params) do def follow(%{assigns: %{user: user}} = conn, params) do
case TwitterAPI.follow(user, params) do case TwitterAPI.follow(user, params) do
{:ok, user, followed, _activity} -> {:ok, user, followed, _activity} ->

View File

@ -0,0 +1,65 @@
defmodule Pleroma.Web.TwitterAPI.NotificationView do
use Pleroma.Web, :view
alias Pleroma.{Notification, User}
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.MediaProxy
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.TwitterAPI.ActivityView
defp get_user(ap_id, opts) do
cond do
user = opts[:users][ap_id] ->
user
String.ends_with?(ap_id, "/followers") ->
nil
ap_id == "https://www.w3.org/ns/activitystreams#Public" ->
nil
true ->
User.get_cached_by_ap_id(ap_id)
end
end
def render("notification.json", %{notifications: notifications, for: user}) do
render_many(
notifications,
Pleroma.Web.TwitterAPI.NotificationView,
"notification.json",
for: user
)
end
def render(
"notification.json",
%{
notification: %Notification{
id: id,
seen: seen,
activity: activity,
inserted_at: created_at
},
for: user
} = opts
) do
ntype =
case activity.data["type"] do
"Create" -> "mention"
"Like" -> "like"
"Announce" -> "repeat"
"Follow" -> "follow"
end
from = get_user(activity.data["actor"], opts)
%{
"id" => id,
"ntype" => ntype,
"notice" => ActivityView.render("activity.json", %{activity: activity, for: user}),
"from_profile" => UserView.render("show.json", %{user: from, for: user}),
"is_seen" => if(seen, do: 1, else: 0),
"created_at" => created_at |> Utils.format_naive_asctime()
}
end
end

View File

@ -1 +1 @@
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta name=viewport content="width=device-width,initial-scale=1"><title>Pleroma</title><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.b96f1ae65bf0fc0f0564dbc7f12febfd.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.c0906490430c3def2aa2.js></script><script type=text/javascript src=/static/js/vendor.07fdc75424ae7c00c949.js></script><script type=text/javascript src=/static/js/app.be2883b7ed550f51c41d.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=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.6c8da1b0ace79ad8881a0e5e716ec818.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.f80ee14f782f05d77e39.js></script><script type=text/javascript src=/static/js/vendor.ef2aee0b2db579c3a86a.js></script><script type=text/javascript src=/static/js/app.64c4246ae54f6439cb69.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

View File

@ -107,6 +107,54 @@
"css": "attach", "css": "attach",
"code": 59402, "code": 59402,
"src": "entypo" "src": "entypo"
},
{
"uid": "e15f0d620a7897e2035c18c80142f6d9",
"css": "link-ext",
"code": 61582,
"src": "fontawesome"
},
{
"uid": "381da2c2f7fd51f8de877c044d7f439d",
"css": "picture",
"code": 59403,
"src": "fontawesome"
},
{
"uid": "872d9516df93eb6b776cc4d94bd97dac",
"css": "video",
"code": 59404,
"src": "fontawesome"
},
{
"uid": "399ef63b1e23ab1b761dfbb5591fa4da",
"css": "right-open",
"code": 59405,
"src": "fontawesome"
},
{
"uid": "d870630ff8f81e6de3958ecaeac532f2",
"css": "left-open",
"code": 59406,
"src": "fontawesome"
},
{
"uid": "fe6697b391355dec12f3d86d6d490397",
"css": "up-open",
"code": 59407,
"src": "fontawesome"
},
{
"uid": "9c1376672bb4f1ed616fdd78a23667e9",
"css": "comment-empty",
"code": 61669,
"src": "fontawesome"
},
{
"uid": "cd21cbfb28ad4d903cede582157f65dc",
"css": "bell",
"code": 59408,
"src": "fontawesome"
} }
] ]
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -229,11 +229,11 @@ body {
} }
@font-face { @font-face {
font-family: 'fontello'; font-family: 'fontello';
src: url('./font/fontello.eot?97388161'); src: url('./font/fontello.eot?34497073');
src: url('./font/fontello.eot?97388161#iefix') format('embedded-opentype'), src: url('./font/fontello.eot?34497073#iefix') format('embedded-opentype'),
url('./font/fontello.woff?97388161') format('woff'), url('./font/fontello.woff?34497073') format('woff'),
url('./font/fontello.ttf?97388161') format('truetype'), url('./font/fontello.ttf?34497073') format('truetype'),
url('./font/fontello.svg?97388161#fontello') format('svg'); url('./font/fontello.svg?34497073#fontello') format('svg');
font-weight: normal; font-weight: normal;
font-style: normal; font-style: normal;
} }
@ -313,11 +313,23 @@ body {
<div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-logout">&#xe808;</i> <span class="i-name">icon-logout</span><span class="i-code">0xe808</span></div> <div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-logout">&#xe808;</i> <span class="i-name">icon-logout</span><span class="i-code">0xe808</span></div>
<div class="the-icons span3" title="Code: 0xe809"><i class="demo-icon icon-down-open">&#xe809;</i> <span class="i-name">icon-down-open</span><span class="i-code">0xe809</span></div> <div class="the-icons span3" title="Code: 0xe809"><i class="demo-icon icon-down-open">&#xe809;</i> <span class="i-name">icon-down-open</span><span class="i-code">0xe809</span></div>
<div class="the-icons span3" title="Code: 0xe80a"><i class="demo-icon icon-attach">&#xe80a;</i> <span class="i-name">icon-attach</span><span class="i-code">0xe80a</span></div> <div class="the-icons span3" title="Code: 0xe80a"><i class="demo-icon icon-attach">&#xe80a;</i> <span class="i-name">icon-attach</span><span class="i-code">0xe80a</span></div>
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin">&#xe832;</i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div> <div class="the-icons span3" title="Code: 0xe80b"><i class="demo-icon icon-picture">&#xe80b;</i> <span class="i-name">icon-picture</span><span class="i-code">0xe80b</span></div>
</div> </div>
<div class="row"> <div class="row">
<div class="the-icons span3" title="Code: 0xe80c"><i class="demo-icon icon-video">&#xe80c;</i> <span class="i-name">icon-video</span><span class="i-code">0xe80c</span></div>
<div class="the-icons span3" title="Code: 0xe80d"><i class="demo-icon icon-right-open">&#xe80d;</i> <span class="i-name">icon-right-open</span><span class="i-code">0xe80d</span></div>
<div class="the-icons span3" title="Code: 0xe80e"><i class="demo-icon icon-left-open">&#xe80e;</i> <span class="i-name">icon-left-open</span><span class="i-code">0xe80e</span></div>
<div class="the-icons span3" title="Code: 0xe80f"><i class="demo-icon icon-up-open">&#xe80f;</i> <span class="i-name">icon-up-open</span><span class="i-code">0xe80f</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xe810"><i class="demo-icon icon-bell">&#xe810;</i> <span class="i-name">icon-bell</span><span class="i-code">0xe810</span></div>
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin">&#xe832;</i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin">&#xe834;</i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div> <div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin">&#xe834;</i> <span class="i-name">icon-spin4</span><span class="i-code">0xe834</span></div>
<div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext">&#xf08e;</i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu">&#xf0c9;</i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div> <div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu">&#xf0c9;</i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty">&#xf0e5;</i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
<div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply">&#xf112;</i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div> <div class="the-icons span3" title="Code: 0xf112"><i class="demo-icon icon-reply">&#xf112;</i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars">&#xf1e5;</i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div> <div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars">&#xf1e5;</i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
</div> </div>

View File

@ -28,12 +28,28 @@
<glyph glyph-name="attach" unicode="&#xe80a;" d="M244-140q-102 0-170 72-72 70-74 166t84 190l496 496q80 80 174 54 44-12 79-47t47-79q26-96-54-176l-474-474q-40-40-88-46-48-4-80 28-30 24-27 74t47 92l332 334q24 26 50 0t0-50l-332-332q-44-44-20-70 12-8 24-6 24 4 46 26l474 474q50 50 34 108-16 60-76 76-54 14-108-36l-494-494q-66-76-64-143t52-117q50-48 117-50t141 62l496 494q24 24 50 0 26-22 0-48l-496-496q-82-82-186-82z" horiz-adv-x="939" /> <glyph glyph-name="attach" unicode="&#xe80a;" d="M244-140q-102 0-170 72-72 70-74 166t84 190l496 496q80 80 174 54 44-12 79-47t47-79q26-96-54-176l-474-474q-40-40-88-46-48-4-80 28-30 24-27 74t47 92l332 334q24 26 50 0t0-50l-332-332q-44-44-20-70 12-8 24-6 24 4 46 26l474 474q50 50 34 108-16 60-76 76-54 14-108-36l-494-494q-66-76-64-143t52-117q50-48 117-50t141 62l496 494q24 24 50 0 26-22 0-48l-496-496q-82-82-186-82z" horiz-adv-x="939" />
<glyph glyph-name="picture" unicode="&#xe80b;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
<glyph glyph-name="video" unicode="&#xe80c;" d="M214-43v72q0 14-10 25t-25 10h-72q-14 0-25-10t-11-25v-72q0-14 11-25t25-11h72q14 0 25 11t10 25z m0 214v72q0 14-10 25t-25 11h-72q-14 0-25-11t-11-25v-72q0-14 11-25t25-10h72q14 0 25 10t10 25z m0 215v71q0 15-10 25t-25 11h-72q-14 0-25-11t-11-25v-71q0-15 11-25t25-11h72q14 0 25 11t10 25z m572-429v286q0 14-11 25t-25 11h-429q-14 0-25-11t-10-25v-286q0-14 10-25t25-11h429q15 0 25 11t11 25z m-572 643v71q0 15-10 26t-25 10h-72q-14 0-25-10t-11-26v-71q0-14 11-25t25-11h72q14 0 25 11t10 25z m786-643v72q0 14-11 25t-25 10h-71q-15 0-25-10t-11-25v-72q0-14 11-25t25-11h71q15 0 25 11t11 25z m-214 429v285q0 15-11 26t-25 10h-429q-14 0-25-10t-10-26v-285q0-15 10-25t25-11h429q15 0 25 11t11 25z m214-215v72q0 14-11 25t-25 11h-71q-15 0-25-11t-11-25v-72q0-14 11-25t25-10h71q15 0 25 10t11 25z m0 215v71q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-71q0-15 11-25t25-11h71q15 0 25 11t11 25z m0 214v71q0 15-11 26t-25 10h-71q-15 0-25-10t-11-26v-71q0-14 11-25t25-11h71q15 0 25 11t11 25z m71 89v-750q0-37-26-63t-63-26h-893q-36 0-63 26t-26 63v750q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
<glyph glyph-name="right-open" unicode="&#xe80d;" d="M618 361l-414-415q-11-10-25-10t-25 10l-93 93q-11 11-11 25t11 25l296 297-296 296q-11 11-11 25t11 25l93 93q10 11 25 11t25-11l414-414q10-11 10-25t-10-25z" horiz-adv-x="714.3" />
<glyph glyph-name="left-open" unicode="&#xe80e;" d="M654 682l-297-296 297-297q10-10 10-25t-10-25l-93-93q-11-10-25-10t-25 10l-414 415q-11 10-11 25t11 25l414 414q10 11 25 11t25-11l93-93q10-10 10-25t-10-25z" horiz-adv-x="714.3" />
<glyph glyph-name="up-open" unicode="&#xe80f;" d="M939 107l-92-92q-11-10-26-10t-25 10l-296 297-296-297q-11-10-25-10t-25 10l-93 92q-11 11-11 26t11 25l414 414q11 10 25 10t25-10l414-414q11-11 11-25t-11-26z" horiz-adv-x="1000" />
<glyph glyph-name="bell" unicode="&#xe810;" d="M509-96q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m-372 160h726q-149 168-149 465 0 28-13 58t-39 58-67 45-95 17-95-17-67-45-39-58-13-58q0-297-149-465z m827 0q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
<glyph glyph-name="spin3" unicode="&#xe832;" d="M494 850c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" /> <glyph glyph-name="spin3" unicode="&#xe832;" d="M494 850c-266 0-483-210-494-472-1-19 13-20 13-20l84 0c16 0 19 10 19 18 10 199 176 358 378 358 107 0 205-45 273-118l-58-57c-11-12-11-27 5-31l247-50c21-5 46 11 37 44l-58 227c-2 9-16 22-29 13l-65-60c-89 91-214 148-352 148z m409-508c-16 0-19-10-19-18-10-199-176-358-377-358-108 0-205 45-274 118l59 57c10 12 10 27-5 31l-248 50c-21 5-46-11-37-44l58-227c2-9 16-22 30-13l64 60c89-91 214-148 353-148 265 0 482 210 493 473 1 18-13 19-13 19l-84 0z" horiz-adv-x="1000" />
<glyph glyph-name="spin4" unicode="&#xe834;" d="M498 850c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" /> <glyph glyph-name="spin4" unicode="&#xe834;" d="M498 850c-114 0-228-39-320-116l0 0c173 140 428 130 588-31 134-134 164-332 89-495-10-29-5-50 12-68 21-20 61-23 84 0 3 3 12 15 15 24 71 180 33 393-112 539-99 98-228 147-356 147z m-409-274c-14 0-29-5-39-16-3-3-13-15-15-24-71-180-34-393 112-539 185-185 479-195 676-31l0 0c-173-140-428-130-589 31-134 134-163 333-89 495 11 29 6 50-12 68-11 11-27 17-44 16z" horiz-adv-x="1001" />
<glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
<glyph glyph-name="menu" unicode="&#xf0c9;" d="M857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-14-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" /> <glyph glyph-name="menu" unicode="&#xf0c9;" d="M857 100v-71q0-15-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 25t25 11h785q15 0 26-11t10-25z m0 286v-72q0-14-10-25t-26-10h-785q-15 0-25 10t-11 25v72q0 14 11 25t25 10h785q15 0 26-10t10-25z m0 285v-71q0-14-10-25t-26-11h-785q-15 0-25 11t-11 25v71q0 15 11 26t25 10h785q15 0 26-10t10-26z" horiz-adv-x="857.1" />
<glyph glyph-name="comment-empty" unicode="&#xf0e5;" d="M500 636q-114 0-213-39t-157-105-59-142q0-62 40-119t113-98l48-28-15-53q-13-51-39-97 85 36 154 96l24 21 32-3q38-5 72-5 114 0 213 39t157 105 59 142-59 142-157 105-213 39z m500-286q0-97-67-179t-182-130-251-48q-39 0-81 4-110-97-257-135-27-8-63-12h-3q-8 0-15 6t-9 15v1q-2 2 0 6t1 6 2 5l4 5t4 5 4 5q4 5 17 19t20 22 17 22 18 28 15 33 15 42q-88 50-138 123t-51 157q0 97 67 179t182 130 251 48 251-48 182-130 67-179z" horiz-adv-x="1000" />
<glyph glyph-name="reply" unicode="&#xf112;" d="M1000 225q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" /> <glyph glyph-name="reply" unicode="&#xf112;" d="M1000 225q0-93-71-252-1-4-6-13t-7-17-7-12q-7-10-16-10-8 0-13 6t-5 14q0 5 1 15t2 13q3 38 3 69 0 56-10 101t-27 77-45 56-59 39-74 24-86 12-98 3h-125v-143q0-14-10-25t-26-11-25 11l-285 286q-11 10-11 25t11 25l285 286q11 10 25 10t26-10 10-25v-143h125q398 0 488-225 30-75 30-186z" horiz-adv-x="1000" />
<glyph glyph-name="binoculars" unicode="&#xf1e5;" d="M393 671v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" /> <glyph glyph-name="binoculars" unicode="&#xf1e5;" d="M393 671v-428q0-15-11-25t-25-11v-321q0-15-10-25t-26-11h-285q-15 0-25 11t-11 25v285l139 488q4 12 17 12h237z m178 0v-392h-142v392h142z m429-500v-285q0-15-11-25t-25-11h-285q-15 0-25 11t-11 25v321q-15 0-25 11t-11 25v428h237q13 0 17-12z m-589 661v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z m375 0v-125h-197v125q0 8 5 13t13 5h161q8 0 13-5t5-13z" horiz-adv-x="1000" />

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2,9 +2,10 @@ defmodule Pleroma.Web.TwitterAPI.ControllerTest do
use Pleroma.Web.ConnCase use Pleroma.Web.ConnCase
alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter alias Pleroma.Web.TwitterAPI.Representers.ActivityRepresenter
alias Pleroma.Builders.{ActivityBuilder, UserBuilder} alias Pleroma.Builders.{ActivityBuilder, UserBuilder}
alias Pleroma.{Repo, Activity, User, Object} alias Pleroma.{Repo, Activity, User, Object, Notification}
alias Pleroma.Web.ActivityPub.ActivityPub alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Web.TwitterAPI.UserView alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.TwitterAPI.NotificationView
alias Pleroma.Web.CommonAPI alias Pleroma.Web.CommonAPI
alias Pleroma.Web.TwitterAPI.TwitterAPI alias Pleroma.Web.TwitterAPI.TwitterAPI
@ -247,6 +248,35 @@ test "with credentials", %{conn: conn, user: current_user} do
end end
end end
describe "GET /api/qvitter/statuses/notifications.json" do
setup [:valid_user]
test "without valid credentials", %{conn: conn} do
conn = get(conn, "/api/qvitter/statuses/notifications.json")
assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
end
test "with credentials", %{conn: conn, user: current_user} do
{:ok, activity} =
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user})
conn =
conn
|> with_credentials(current_user.nickname, "test")
|> get("/api/qvitter/statuses/notifications.json")
response = json_response(conn, 200)
assert length(response) == 1
assert response ==
NotificationView.render("notification.json", %{
notifications: Notification.for_user(current_user),
for: current_user
})
end
end
describe "GET /statuses/user_timeline.json" do describe "GET /statuses/user_timeline.json" do
setup [:valid_user] setup [:valid_user]

View File

@ -0,0 +1,108 @@
defmodule Pleroma.Web.TwitterAPI.NotificationViewTest do
use Pleroma.DataCase
alias Pleroma.{User, Notification}
alias Pleroma.Web.TwitterAPI.TwitterAPI
alias Pleroma.Web.TwitterAPI.NotificationView
alias Pleroma.Web.TwitterAPI.UserView
alias Pleroma.Web.TwitterAPI.ActivityView
alias Pleroma.Web.CommonAPI.Utils
alias Pleroma.Web.ActivityPub.ActivityPub
alias Pleroma.Builders.UserBuilder
import Pleroma.Factory
setup do
user = insert(:user, bio: "<span>Here's some html</span>")
[user: user]
end
test "A follow notification" do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
follower = insert(:user)
{:ok, follower} = User.follow(follower, user)
{:ok, activity} = ActivityPub.follow(follower, user)
Cachex.set(:user_cache, "user_info:#{user.id}", User.user_info(Repo.get!(User, user.id)))
[follow_notif] = Notification.for_user(user)
represented = %{
"created_at" => follow_notif.inserted_at |> Utils.format_naive_asctime(),
"from_profile" => UserView.render("show.json", %{user: follower, for: user}),
"id" => follow_notif.id,
"is_seen" => 0,
"notice" => ActivityView.render("activity.json", %{activity: activity, for: user}),
"ntype" => "follow"
}
assert represented ==
NotificationView.render("notification.json", %{notification: follow_notif, for: user})
end
test "A mention notification" do
user = insert(:user)
other_user = insert(:user)
{:ok, activity} =
TwitterAPI.create_status(other_user, %{"status" => "Päivää, @#{user.nickname}"})
[notification] = Notification.for_user(user)
represented = %{
"created_at" => notification.inserted_at |> Utils.format_naive_asctime(),
"from_profile" => UserView.render("show.json", %{user: other_user, for: user}),
"id" => notification.id,
"is_seen" => 0,
"notice" => ActivityView.render("activity.json", %{activity: activity, for: user}),
"ntype" => "mention"
}
assert represented ==
NotificationView.render("notification.json", %{notification: notification, for: user})
end
test "A retweet notification" do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
repeater = insert(:user)
{:ok, activity} = TwitterAPI.repeat(repeater, note_activity.id)
[notification] = Notification.for_user(user)
represented = %{
"created_at" => notification.inserted_at |> Utils.format_naive_asctime(),
"from_profile" => UserView.render("show.json", %{user: repeater, for: user}),
"id" => notification.id,
"is_seen" => 0,
"notice" =>
ActivityView.render("activity.json", %{activity: notification.activity, for: user}),
"ntype" => "repeat"
}
assert represented ==
NotificationView.render("notification.json", %{notification: notification, for: user})
end
test "A like notification" do
note_activity = insert(:note_activity)
user = User.get_cached_by_ap_id(note_activity.data["actor"])
liker = insert(:user)
{:ok, activity} = TwitterAPI.fav(liker, note_activity.id)
[notification] = Notification.for_user(user)
represented = %{
"created_at" => notification.inserted_at |> Utils.format_naive_asctime(),
"from_profile" => UserView.render("show.json", %{user: liker, for: user}),
"id" => notification.id,
"is_seen" => 0,
"notice" =>
ActivityView.render("activity.json", %{activity: notification.activity, for: user}),
"ntype" => "like"
}
assert represented ==
NotificationView.render("notification.json", %{notification: notification, for: user})
end
end