forked from AkkomaGang/akkoma
Merge branch 'develop' into feature/mstdn-direct-api
This commit is contained in:
commit
4fd9df100f
99 changed files with 1602 additions and 103 deletions
|
@ -74,7 +74,7 @@ This is useful for running pleroma inside Tor or i2p.
|
||||||
|
|
||||||
### Register a User
|
### Register a User
|
||||||
|
|
||||||
Run `mix register_user <name> <nickname> <email> <bio>`. The `name` appears on statuses, while the nickname corresponds to the user, e.g. `@nickname@instance.tld`
|
Run `mix register_user <name> <nickname> <email> <bio> <password>`. The `name` appears on statuses, while the nickname corresponds to the user, e.g. `@nickname@instance.tld`
|
||||||
|
|
||||||
### Password reset
|
### Password reset
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,16 @@ server {
|
||||||
}
|
}
|
||||||
# stop removing lines here.
|
# stop removing lines here.
|
||||||
|
|
||||||
|
add_header X-XSS-Protection "1; mode=block";
|
||||||
|
add_header X-Permitted-Cross-Domain-Policies none;
|
||||||
|
add_header X-Frame-Options DENY;
|
||||||
|
add_header X-Content-Type-Options nosniff;
|
||||||
|
add_header Referrer-Policy same-origin;
|
||||||
|
add_header X-Download-Options noopen;
|
||||||
|
|
||||||
|
# Uncomment this only after you get HTTPS working.
|
||||||
|
# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";
|
||||||
|
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
proxy_set_header Upgrade $http_upgrade;
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
proxy_set_header Connection "upgrade";
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
|
@ -13,4 +13,3 @@ Restart=on-failure
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
Alias=pleroma.service
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ sub vcl_recv {
|
||||||
|
|
||||||
# Strip headers that will affect caching from all other static content
|
# Strip headers that will affect caching from all other static content
|
||||||
# This also permits caching of individual toots and AP Activities
|
# This also permits caching of individual toots and AP Activities
|
||||||
if ((req.url ~ "^/(media|notice|static)/") ||
|
if ((req.url ~ "^/(media|static)/") ||
|
||||||
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
|
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
|
||||||
{
|
{
|
||||||
unset req.http.Cookie;
|
unset req.http.Cookie;
|
||||||
|
@ -93,8 +93,7 @@ sub vcl_backend_response {
|
||||||
|
|
||||||
# Strip cache-restricting headers from Pleroma on static content that we want to cache
|
# Strip cache-restricting headers from Pleroma on static content that we want to cache
|
||||||
# Also enable streaming of cached content to clients (no waiting for Varnish to complete backend fetch)
|
# Also enable streaming of cached content to clients (no waiting for Varnish to complete backend fetch)
|
||||||
if ((bereq.url ~ "^/(notice)/") ||
|
if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$")
|
||||||
(bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
|
|
||||||
{
|
{
|
||||||
unset beresp.http.set-cookie;
|
unset beresp.http.set-cookie;
|
||||||
unset beresp.http.Cache-Control;
|
unset beresp.http.Cache-Control;
|
||||||
|
|
13
lib/mix/tasks/deactivate_user.ex
Normal file
13
lib/mix/tasks/deactivate_user.ex
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
defmodule Mix.Tasks.DeactivateUser do
|
||||||
|
use Mix.Task
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
|
@shortdoc "Toggle deactivation status for a user"
|
||||||
|
def run([nickname]) do
|
||||||
|
Mix.Task.run("app.start")
|
||||||
|
|
||||||
|
with user <- User.get_by_nickname(nickname) do
|
||||||
|
User.deactivate(user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -6,3 +6,4 @@ ALTER DATABASE pleroma_dev OWNER TO pleroma;
|
||||||
\c pleroma_dev;
|
\c pleroma_dev;
|
||||||
--Extensions made by ecto.migrate that need superuser access
|
--Extensions made by ecto.migrate that need superuser access
|
||||||
CREATE EXTENSION IF NOT EXISTS citext;
|
CREATE EXTENSION IF NOT EXISTS citext;
|
||||||
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
||||||
|
|
|
@ -160,6 +160,7 @@ def add_links({subs, text}) do
|
||||||
links =
|
links =
|
||||||
Regex.scan(@link_regex, text)
|
Regex.scan(@link_regex, text)
|
||||||
|> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end)
|
|> Enum.map(fn [url] -> {Ecto.UUID.generate(), url} end)
|
||||||
|
|> Enum.sort_by(fn {_, url} -> -String.length(url) end)
|
||||||
|
|
||||||
uuid_text =
|
uuid_text =
|
||||||
links
|
links
|
||||||
|
|
|
@ -91,7 +91,8 @@ def create_notifications(_), do: {:ok, []}
|
||||||
|
|
||||||
# TODO move to sql, too.
|
# TODO move to sql, too.
|
||||||
def create_notification(%Activity{} = activity, %User{} = user) do
|
def create_notification(%Activity{} = activity, %User{} = user) do
|
||||||
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) do
|
unless User.blocks?(user, %{ap_id: activity.data["actor"]}) or
|
||||||
|
user.ap_id == activity.data["actor"] do
|
||||||
notification = %Notification{user_id: user.id, activity: activity}
|
notification = %Notification{user_id: user.id, activity: activity}
|
||||||
{:ok, notification} = Repo.insert(notification)
|
{:ok, notification} = Repo.insert(notification)
|
||||||
Pleroma.Web.Streamer.stream("user", notification)
|
Pleroma.Web.Streamer.stream("user", notification)
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
defmodule Pleroma.Web.Plugs.HTTPSignaturePlug do
|
||||||
alias Pleroma.Web.HTTPSignatures
|
alias Pleroma.Web.HTTPSignatures
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ def call(%{assigns: %{valid_signature: true}} = conn, _opts) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def call(conn, _opts) do
|
def call(conn, _opts) do
|
||||||
user = conn.params["actor"]
|
user = Utils.normalize_actor(conn.params["actor"])
|
||||||
Logger.debug("Checking sig for #{user}")
|
Logger.debug("Checking sig for #{user}")
|
||||||
[signature | _] = get_req_header(conn, "signature")
|
[signature | _] = get_req_header(conn, "signature")
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ defmodule Pleroma.User do
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
field(:info, :map, default: %{})
|
field(:info, :map, default: %{})
|
||||||
field(:follower_address, :string)
|
field(:follower_address, :string)
|
||||||
|
field(:search_distance, :float, virtual: true)
|
||||||
has_many(:notifications, Notification)
|
has_many(:notifications, Notification)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
|
@ -399,16 +400,24 @@ def search(query, resolve) do
|
||||||
User.get_or_fetch_by_nickname(query)
|
User.get_or_fetch_by_nickname(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
q =
|
inner =
|
||||||
from(
|
from(
|
||||||
u in User,
|
u in User,
|
||||||
where:
|
select_merge: %{
|
||||||
|
search_distance:
|
||||||
fragment(
|
fragment(
|
||||||
"(to_tsvector('english', ?) || to_tsvector('english', ?)) @@ plainto_tsquery('english', ?)",
|
"? <-> (? || ?)",
|
||||||
|
^query,
|
||||||
u.nickname,
|
u.nickname,
|
||||||
u.name,
|
u.name
|
||||||
^query
|
)
|
||||||
),
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
q =
|
||||||
|
from(
|
||||||
|
s in subquery(inner),
|
||||||
|
order_by: s.search_distance,
|
||||||
limit: 20
|
limit: 20
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification}
|
alias Pleroma.{Activity, Repo, Object, Upload, User, Notification}
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.{Transmogrifier, MRF}
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
alias Pleroma.Web.Federator
|
alias Pleroma.Web.Federator
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
|
@ -11,16 +11,29 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
@httpoison Application.get_env(:pleroma, :httpoison)
|
@httpoison Application.get_env(:pleroma, :httpoison)
|
||||||
|
|
||||||
@instance Application.get_env(:pleroma, :instance)
|
@instance Application.get_env(:pleroma, :instance)
|
||||||
@rewrite_policy Keyword.get(@instance, :rewrite_policy)
|
|
||||||
|
|
||||||
def get_recipients(data) do
|
def get_recipients(data) do
|
||||||
(data["to"] || []) ++ (data["cc"] || [])
|
(data["to"] || []) ++ (data["cc"] || [])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp check_actor_is_active(actor) do
|
||||||
|
if not is_nil(actor) do
|
||||||
|
with user <- User.get_cached_by_ap_id(actor),
|
||||||
|
nil <- user.info["deactivated"] do
|
||||||
|
:ok
|
||||||
|
else
|
||||||
|
_e -> :reject
|
||||||
|
end
|
||||||
|
else
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def insert(map, local \\ true) when is_map(map) do
|
def insert(map, local \\ true) when is_map(map) do
|
||||||
with nil <- Activity.get_by_ap_id(map["id"]),
|
with nil <- Activity.get_by_ap_id(map["id"]),
|
||||||
map <- lazy_put_activity_defaults(map),
|
map <- lazy_put_activity_defaults(map),
|
||||||
{:ok, map} <- @rewrite_policy.filter(map),
|
:ok <- check_actor_is_active(map["actor"]),
|
||||||
|
{:ok, map} <- MRF.filter(map),
|
||||||
:ok <- insert_full_object(map) do
|
:ok <- insert_full_object(map) do
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
Repo.insert(%Activity{
|
Repo.insert(%Activity{
|
||||||
|
@ -127,11 +140,19 @@ def like(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unlike(%User{} = actor, %Object{} = object) do
|
def unlike(
|
||||||
with %Activity{} = activity <- get_existing_like(actor.ap_id, object),
|
%User{} = actor,
|
||||||
{:ok, _activity} <- Repo.delete(activity),
|
%Object{} = object,
|
||||||
{:ok, object} <- remove_like_from_object(activity, object) do
|
activity_id \\ nil,
|
||||||
{:ok, object}
|
local \\ true
|
||||||
|
) do
|
||||||
|
with %Activity{} = like_activity <- get_existing_like(actor.ap_id, object),
|
||||||
|
unlike_data <- make_unlike_data(actor, like_activity, activity_id),
|
||||||
|
{:ok, unlike_activity} <- insert(unlike_data, local),
|
||||||
|
{:ok, _activity} <- Repo.delete(like_activity),
|
||||||
|
{:ok, object} <- remove_like_from_object(like_activity, object),
|
||||||
|
:ok <- maybe_federate(unlike_activity) do
|
||||||
|
{:ok, unlike_activity, like_activity, object}
|
||||||
else
|
else
|
||||||
_e -> {:ok, object}
|
_e -> {:ok, object}
|
||||||
end
|
end
|
||||||
|
@ -154,6 +175,24 @@ def announce(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unannounce(
|
||||||
|
%User{} = actor,
|
||||||
|
%Object{} = object,
|
||||||
|
activity_id \\ nil,
|
||||||
|
local \\ true
|
||||||
|
) do
|
||||||
|
with %Activity{} = announce_activity <- get_existing_announce(actor.ap_id, object),
|
||||||
|
unannounce_data <- make_unannounce_data(actor, announce_activity, activity_id),
|
||||||
|
{:ok, unannounce_activity} <- insert(unannounce_data, local),
|
||||||
|
:ok <- maybe_federate(unannounce_activity),
|
||||||
|
{:ok, _activity} <- Repo.delete(announce_activity),
|
||||||
|
{:ok, object} <- remove_announce_from_object(announce_activity, object) do
|
||||||
|
{:ok, unannounce_activity, announce_activity, object}
|
||||||
|
else
|
||||||
|
_e -> {:ok, object}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
def follow(follower, followed, activity_id \\ nil, local \\ true) do
|
||||||
with data <- make_follow_data(follower, followed, activity_id),
|
with data <- make_follow_data(follower, followed, activity_id),
|
||||||
{:ok, activity} <- insert(data, local),
|
{:ok, activity} <- insert(data, local),
|
||||||
|
@ -221,11 +260,11 @@ def fetch_activities_for_context(context, opts \\ %{}) do
|
||||||
Repo.all(query)
|
Repo.all(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: Make this work properly with unlisted.
|
|
||||||
def fetch_public_activities(opts \\ %{}) do
|
def fetch_public_activities(opts \\ %{}) do
|
||||||
q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
|
q = fetch_activities_query(["https://www.w3.org/ns/activitystreams#Public"], opts)
|
||||||
|
|
||||||
q
|
q
|
||||||
|
|> restrict_unlisted()
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
|> Enum.reverse()
|
|> Enum.reverse()
|
||||||
end
|
end
|
||||||
|
@ -254,6 +293,25 @@ defp restrict_visibility(_query, %{visibility: visibility})
|
||||||
|
|
||||||
defp restrict_visibility(query, _visibility), do: query
|
defp restrict_visibility(query, _visibility), do: query
|
||||||
|
|
||||||
|
def fetch_user_activities(user, reading_user, params \\ %{}) do
|
||||||
|
params =
|
||||||
|
params
|
||||||
|
|> Map.put("type", ["Create", "Announce"])
|
||||||
|
|> Map.put("actor_id", user.ap_id)
|
||||||
|
|> Map.put("whole_db", true)
|
||||||
|
|
||||||
|
recipients =
|
||||||
|
if reading_user do
|
||||||
|
["https://www.w3.org/ns/activitystreams#Public"] ++
|
||||||
|
[reading_user.ap_id | reading_user.following]
|
||||||
|
else
|
||||||
|
["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
end
|
||||||
|
|
||||||
|
fetch_activities(recipients, params)
|
||||||
|
|> Enum.reverse()
|
||||||
|
end
|
||||||
|
|
||||||
defp restrict_since(query, %{"since_id" => since_id}) do
|
defp restrict_since(query, %{"since_id" => since_id}) do
|
||||||
from(activity in query, where: activity.id > ^since_id)
|
from(activity in query, where: activity.id > ^since_id)
|
||||||
end
|
end
|
||||||
|
@ -356,6 +414,18 @@ defp restrict_blocked(query, %{"blocking_user" => %User{info: info}}) do
|
||||||
|
|
||||||
defp restrict_blocked(query, _), do: query
|
defp restrict_blocked(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_unlisted(query) do
|
||||||
|
from(
|
||||||
|
activity in query,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"not (coalesce(?->'cc', '{}'::jsonb) \\?| ?)",
|
||||||
|
activity.data,
|
||||||
|
^["https://www.w3.org/ns/activitystreams#Public"]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def fetch_activities_query(recipients, opts \\ %{}) do
|
def fetch_activities_query(recipients, opts \\ %{}) do
|
||||||
base_query =
|
base_query =
|
||||||
from(
|
from(
|
||||||
|
@ -406,6 +476,8 @@ def user_data_from_user_object(data) do
|
||||||
"url" => [%{"href" => data["image"]["url"]}]
|
"url" => [%{"href" => data["image"]["url"]}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data = Transmogrifier.maybe_fix_user_object(data)
|
||||||
|
|
||||||
user_data = %{
|
user_data = %{
|
||||||
ap_id: data["id"],
|
ap_id: data["id"],
|
||||||
info: %{
|
info: %{
|
||||||
|
|
|
@ -93,7 +93,7 @@ def inbox(conn, params) do
|
||||||
Logger.info("Signature not from author, relayed message, fetching from source")
|
Logger.info("Signature not from author, relayed message, fetching from source")
|
||||||
ActivityPub.fetch_object_from_id(params["object"]["id"])
|
ActivityPub.fetch_object_from_id(params["object"]["id"])
|
||||||
else
|
else
|
||||||
Logger.info("Signature error")
|
Logger.info("Signature error - make sure you are forwarding the HTTP Host header!")
|
||||||
Logger.info("Could not validate #{params["actor"]}")
|
Logger.info("Could not validate #{params["actor"]}")
|
||||||
Logger.info(inspect(conn.req_headers))
|
Logger.info(inspect(conn.req_headers))
|
||||||
end
|
end
|
||||||
|
|
24
lib/pleroma/web/activity_pub/mrf.ex
Normal file
24
lib/pleroma/web/activity_pub/mrf.ex
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
defmodule Pleroma.Web.ActivityPub.MRF do
|
||||||
|
@callback filter(Map.t()) :: {:ok | :reject, Map.t()}
|
||||||
|
|
||||||
|
def filter(object) do
|
||||||
|
get_policies()
|
||||||
|
|> Enum.reduce({:ok, object}, fn
|
||||||
|
policy, {:ok, object} ->
|
||||||
|
policy.filter(object)
|
||||||
|
|
||||||
|
_, error ->
|
||||||
|
error
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_policies() do
|
||||||
|
Application.get_env(:pleroma, :instance, [])
|
||||||
|
|> Keyword.get(:rewrite_policy, [])
|
||||||
|
|> get_policies()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp get_policies(policy) when is_atom(policy), do: [policy]
|
||||||
|
defp get_policies(policies) when is_list(policies), do: policies
|
||||||
|
defp get_policies(_), do: []
|
||||||
|
end
|
|
@ -1,6 +1,8 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.DropPolicy do
|
||||||
require Logger
|
require Logger
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
|
@impl true
|
||||||
def filter(object) do
|
def filter(object) do
|
||||||
Logger.info("REJECTING #{inspect(object)}")
|
Logger.info("REJECTING #{inspect(object)}")
|
||||||
{:reject, object}
|
{:reject, object}
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.NoOpPolicy do
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
|
@impl true
|
||||||
def filter(object) do
|
def filter(object) do
|
||||||
{:ok, object}
|
{:ok, object}
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
defmodule Pleroma.Web.ActivityPub.MRF.SimplePolicy do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@behaviour Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
|
@mrf_policy Application.get_env(:pleroma, :mrf_simple)
|
||||||
|
|
||||||
|
@ -69,6 +70,7 @@ defp check_ftl_removal(actor_info, object) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@impl true
|
||||||
def filter(object) do
|
def filter(object) do
|
||||||
actor_info = URI.parse(object["actor"])
|
actor_info = URI.parse(object["actor"])
|
||||||
|
|
||||||
|
|
|
@ -223,9 +223,45 @@ def handle_incoming(
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{
|
||||||
|
"type" => "Undo",
|
||||||
|
"object" => %{"type" => "Announce", "object" => object_id},
|
||||||
|
"actor" => actor,
|
||||||
|
"id" => id
|
||||||
|
} = data
|
||||||
|
) do
|
||||||
|
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||||
|
{:ok, object} <-
|
||||||
|
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
|
||||||
|
{:ok, activity, _, _} <- ActivityPub.unannounce(actor, object, id, false) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
e -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle_incoming(
|
||||||
|
%{
|
||||||
|
"type" => "Undo",
|
||||||
|
"object" => %{"type" => "Like", "object" => object_id},
|
||||||
|
"actor" => actor,
|
||||||
|
"id" => id
|
||||||
|
} = data
|
||||||
|
) do
|
||||||
|
with %User{} = actor <- User.get_or_fetch_by_ap_id(actor),
|
||||||
|
{:ok, object} <-
|
||||||
|
get_obj_helper(object_id) || ActivityPub.fetch_object_from_id(object_id),
|
||||||
|
{:ok, activity, _, _} <- ActivityPub.unlike(actor, object, id, false) do
|
||||||
|
{:ok, activity}
|
||||||
|
else
|
||||||
|
e -> :error
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
# Accept
|
# Accept
|
||||||
# Undo
|
# Undo for non-Announce
|
||||||
|
|
||||||
def handle_incoming(_), do: :error
|
def handle_incoming(_), do: :error
|
||||||
|
|
||||||
|
@ -477,4 +513,17 @@ def maybe_retire_websub(ap_id) do
|
||||||
Repo.delete_all(q)
|
Repo.delete_all(q)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def maybe_fix_user_url(data) do
|
||||||
|
if is_map(data["url"]) do
|
||||||
|
data = Map.put(data, "url", data["url"]["href"])
|
||||||
|
end
|
||||||
|
|
||||||
|
data
|
||||||
|
end
|
||||||
|
|
||||||
|
def maybe_fix_user_object(data) do
|
||||||
|
data
|
||||||
|
|> maybe_fix_user_url
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,22 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
alias Ecto.{Changeset, UUID}
|
alias Ecto.{Changeset, UUID}
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
||||||
|
# Some implementations send the actor URI as the actor field, others send the entire actor object,
|
||||||
|
# so figure out what the actor's URI is based on what we have.
|
||||||
|
def normalize_actor(actor) do
|
||||||
|
cond do
|
||||||
|
is_binary(actor) ->
|
||||||
|
actor
|
||||||
|
|
||||||
|
is_map(actor) ->
|
||||||
|
actor["id"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def normalize_params(params) do
|
||||||
|
Map.put(params, "actor", normalize_actor(params["actor"]))
|
||||||
|
end
|
||||||
|
|
||||||
def make_json_ld_header do
|
def make_json_ld_header do
|
||||||
%{
|
%{
|
||||||
"@context" => [
|
"@context" => [
|
||||||
|
@ -237,6 +253,28 @@ def fetch_latest_follow(%User{ap_id: follower_id}, %User{ap_id: followed_id}) do
|
||||||
|
|
||||||
#### Announce-related helpers
|
#### Announce-related helpers
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Retruns an existing announce activity if the notice has already been announced
|
||||||
|
"""
|
||||||
|
def get_existing_announce(actor, %{data: %{"id" => id}}) do
|
||||||
|
query =
|
||||||
|
from(
|
||||||
|
activity in Activity,
|
||||||
|
where: fragment("(?)->>'actor' = ?", activity.data, ^actor),
|
||||||
|
# this is to use the index
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
activity.data,
|
||||||
|
activity.data,
|
||||||
|
^id
|
||||||
|
),
|
||||||
|
where: fragment("(?)->>'type' = 'Announce'", activity.data)
|
||||||
|
)
|
||||||
|
|
||||||
|
Repo.one(query)
|
||||||
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Make announce activity data for the given actor and object
|
Make announce activity data for the given actor and object
|
||||||
"""
|
"""
|
||||||
|
@ -257,12 +295,55 @@ def make_announce_data(
|
||||||
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Make unannounce activity data for the given actor and object
|
||||||
|
"""
|
||||||
|
def make_unannounce_data(
|
||||||
|
%User{ap_id: ap_id} = user,
|
||||||
|
%Activity{data: %{"context" => context}} = activity,
|
||||||
|
activity_id
|
||||||
|
) do
|
||||||
|
data = %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"actor" => ap_id,
|
||||||
|
"object" => activity.data,
|
||||||
|
"to" => [user.follower_address, activity.data["actor"]],
|
||||||
|
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"context" => context
|
||||||
|
}
|
||||||
|
|
||||||
|
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||||
|
end
|
||||||
|
|
||||||
|
def make_unlike_data(
|
||||||
|
%User{ap_id: ap_id} = user,
|
||||||
|
%Activity{data: %{"context" => context}} = activity,
|
||||||
|
activity_id
|
||||||
|
) do
|
||||||
|
data = %{
|
||||||
|
"type" => "Undo",
|
||||||
|
"actor" => ap_id,
|
||||||
|
"object" => activity.data,
|
||||||
|
"to" => [user.follower_address, activity.data["actor"]],
|
||||||
|
"cc" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||||
|
"context" => context
|
||||||
|
}
|
||||||
|
|
||||||
|
if activity_id, do: Map.put(data, "id", activity_id), else: data
|
||||||
|
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
|
with announcements <- [actor | object.data["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
|
||||||
|
with announcements <- (object.data["announcements"] || []) |> List.delete(actor) do
|
||||||
|
update_element_in_object("announcement", announcements, object)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
#### Unfollow-related helpers
|
#### Unfollow-related helpers
|
||||||
|
|
||||||
def make_unfollow_data(follower, followed, follow_activity) do
|
def make_unfollow_data(follower, followed, follow_activity) do
|
||||||
|
|
|
@ -24,6 +24,16 @@ def repeat(id_or_ap_id, user) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unrepeat(id_or_ap_id, user) do
|
||||||
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
|
object <- Object.get_by_ap_id(activity.data["object"]["id"]) do
|
||||||
|
ActivityPub.unannounce(user, object)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
{:error, "Could not unrepeat"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def favorite(id_or_ap_id, user) do
|
def favorite(id_or_ap_id, user) do
|
||||||
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
with %Activity{} = activity <- get_by_id_or_ap_id(id_or_ap_id),
|
||||||
false <- activity.data["actor"] == user.ap_id,
|
false <- activity.data["actor"] == user.ap_id,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
defmodule Pleroma.Web.CommonAPI.Utils do
|
defmodule Pleroma.Web.CommonAPI.Utils do
|
||||||
alias Pleroma.{Repo, Object, Formatter, Activity}
|
alias Pleroma.{Repo, Object, Formatter, Activity}
|
||||||
alias Pleroma.Web.ActivityPub.Utils
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
|
alias Pleroma.User
|
||||||
alias Calendar.Strftime
|
alias Calendar.Strftime
|
||||||
|
alias Comeonin.Pbkdf2
|
||||||
|
|
||||||
# This is a hack for twidere.
|
# This is a hack for twidere.
|
||||||
def get_by_id_or_ap_id(id) do
|
def get_by_id_or_ap_id(id) do
|
||||||
|
@ -184,4 +186,13 @@ defp shortname(name) do
|
||||||
String.slice(name, 0..30) <> "…"
|
String.slice(name, 0..30) <> "…"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def confirm_current_password(user, params) do
|
||||||
|
with %User{local: true} = db_user <- Repo.get(User, user.id),
|
||||||
|
true <- Pbkdf2.checkpw(params["password"], db_user.password_hash) do
|
||||||
|
{:ok, db_user}
|
||||||
|
else
|
||||||
|
_ -> {:error, "Invalid password."}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ defmodule Pleroma.Web.Federator do
|
||||||
alias Pleroma.Web.{WebFinger, Websub}
|
alias Pleroma.Web.{WebFinger, Websub}
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@websub Application.get_env(:pleroma, :websub)
|
@websub Application.get_env(:pleroma, :websub)
|
||||||
|
@ -91,6 +92,8 @@ def handle(:incoming_doc, doc) do
|
||||||
def handle(:incoming_ap_doc, params) do
|
def handle(:incoming_ap_doc, params) do
|
||||||
Logger.info("Handling incoming AP activity")
|
Logger.info("Handling incoming AP activity")
|
||||||
|
|
||||||
|
params = Utils.normalize_params(params)
|
||||||
|
|
||||||
with {:ok, _user} <- ap_enabled_actor(params["actor"]),
|
with {:ok, _user} <- ap_enabled_actor(params["actor"]),
|
||||||
nil <- Activity.get_by_ap_id(params["id"]),
|
nil <- Activity.get_by_ap_id(params["id"]),
|
||||||
{:ok, _activity} <- Transmogrifier.handle_incoming(params) do
|
{:ok, _activity} <- Transmogrifier.handle_incoming(params) do
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
# https://tools.ietf.org/html/draft-cavage-http-signatures-08
|
||||||
defmodule Pleroma.Web.HTTPSignatures do
|
defmodule Pleroma.Web.HTTPSignatures do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
|
@ -31,14 +32,14 @@ def validate(headers, signature, public_key) do
|
||||||
def validate_conn(conn) do
|
def validate_conn(conn) do
|
||||||
# TODO: How to get the right key and see if it is actually valid for that request.
|
# TODO: How to get the right key and see if it is actually valid for that request.
|
||||||
# For now, fetch the key for the actor.
|
# For now, fetch the key for the actor.
|
||||||
with actor_id <- conn.params["actor"],
|
with actor_id <- Utils.normalize_actor(conn.params["actor"]),
|
||||||
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
||||||
if validate_conn(conn, public_key) do
|
if validate_conn(conn, public_key) do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
Logger.debug("Could not validate, re-fetching user and trying one more time")
|
Logger.debug("Could not validate, re-fetching user and trying one more time")
|
||||||
# Fetch user anew and try one more time
|
# Fetch user anew and try one more time
|
||||||
with actor_id <- conn.params["actor"],
|
with actor_id <- Utils.normalize_actor(conn.params["actor"]),
|
||||||
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
|
{:ok, _user} <- ActivityPub.make_user_from_ap_id(actor_id),
|
||||||
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
{:ok, public_key} <- User.get_public_key_for_ap_id(actor_id) do
|
||||||
validate_conn(conn, public_key)
|
validate_conn(conn, public_key)
|
||||||
|
|
|
@ -204,21 +204,14 @@ def public_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
|
|> render(StatusView, "index.json", %{activities: activities, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
||||||
def user_statuses(%{assigns: %{user: user}} = conn, params) do
|
def user_statuses(%{assigns: %{user: reading_user}} = conn, params) do
|
||||||
with %User{ap_id: ap_id} <- Repo.get(User, params["id"]) do
|
with %User{} = user <- Repo.get(User, params["id"]) do
|
||||||
params =
|
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
|
||||||
params
|
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|
||||||
|> Map.put("actor_id", ap_id)
|
|
||||||
|> Map.put("whole_db", true)
|
|
||||||
|
|
||||||
activities =
|
activities =
|
||||||
if params["pinned"] == "true" do
|
if params["pinned"] == "true" do
|
||||||
# Since Pleroma has no "pinned" posts feature, we'll just set an empty list here
|
|
||||||
[]
|
[]
|
||||||
else
|
else
|
||||||
ActivityPub.fetch_public_activities(params)
|
ActivityPub.fetch_user_activities(user, reading_user, params)
|
||||||
|> Enum.reverse()
|
|
||||||
end
|
end
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|
@ -317,6 +310,13 @@ def reblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unreblog_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
|
with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unrepeat(ap_id_or_id, user),
|
||||||
|
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||||
|
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||||
|
@ -325,7 +325,7 @@ def fav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
def unfav_status(%{assigns: %{user: user}} = conn, %{"id" => ap_id_or_id}) do
|
||||||
with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
with {:ok, _, _, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||||
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
|
render(conn, StatusView, "status.json", %{activity: activity, for: user, as: :activity})
|
||||||
end
|
end
|
||||||
|
|
|
@ -239,10 +239,17 @@ def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) d
|
||||||
inserted_at = activity.data["published"]
|
inserted_at = activity.data["published"]
|
||||||
|
|
||||||
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
author = if with_author, do: [{:author, UserRepresenter.to_simple_form(user)}], else: []
|
||||||
follow_activity = Activity.get_by_ap_id(activity.data["object"])
|
|
||||||
|
follow_activity =
|
||||||
|
if is_map(activity.data["object"]) do
|
||||||
|
Activity.get_by_ap_id(activity.data["object"]["id"])
|
||||||
|
else
|
||||||
|
Activity.get_by_ap_id(activity.data["object"])
|
||||||
|
end
|
||||||
|
|
||||||
mentions = (activity.recipients || []) |> get_mentions
|
mentions = (activity.recipients || []) |> get_mentions
|
||||||
|
|
||||||
|
if follow_activity do
|
||||||
[
|
[
|
||||||
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
|
{:"activity:object-type", ['http://activitystrea.ms/schema/1.0/activity']},
|
||||||
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
|
{:"activity:verb", ['http://activitystrea.ms/schema/1.0/unfollow']},
|
||||||
|
@ -261,6 +268,7 @@ def to_simple_form(%{data: %{"type" => "Undo"}} = activity, user, with_author) d
|
||||||
{:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
|
{:link, [rel: 'self', type: ['application/atom+xml'], href: h.(activity.data["id"])], []}
|
||||||
] ++ mentions ++ author
|
] ++ mentions ++ author
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do
|
def to_simple_form(%{data: %{"type" => "Delete"}} = activity, user, with_author) do
|
||||||
h = fn str -> [to_charlist(str)] end
|
h = fn str -> [to_charlist(str)] end
|
||||||
|
|
|
@ -73,6 +73,7 @@ def user_fetcher(username) do
|
||||||
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
scope "/api/pleroma", Pleroma.Web.TwitterAPI do
|
||||||
pipe_through(:authenticated_api)
|
pipe_through(:authenticated_api)
|
||||||
post("/follow_import", UtilController, :follow_import)
|
post("/follow_import", UtilController, :follow_import)
|
||||||
|
post("/delete_account", UtilController, :delete_account)
|
||||||
end
|
end
|
||||||
|
|
||||||
scope "/oauth", Pleroma.Web.OAuth do
|
scope "/oauth", Pleroma.Web.OAuth do
|
||||||
|
@ -114,6 +115,7 @@ def user_fetcher(username) do
|
||||||
delete("/statuses/:id", MastodonAPIController, :delete_status)
|
delete("/statuses/:id", MastodonAPIController, :delete_status)
|
||||||
|
|
||||||
post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
|
post("/statuses/:id/reblog", MastodonAPIController, :reblog_status)
|
||||||
|
post("/statuses/:id/unreblog", MastodonAPIController, :unreblog_status)
|
||||||
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
post("/statuses/:id/favourite", MastodonAPIController, :fav_status)
|
||||||
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
post("/statuses/:id/unfavourite", MastodonAPIController, :unfav_status)
|
||||||
|
|
||||||
|
|
|
@ -187,13 +187,14 @@ def publish(user, activity, poster \\ &@httpoison.post/4)
|
||||||
|
|
||||||
def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
def publish(%{info: %{"keys" => keys}} = user, %{data: %{"type" => type}} = activity, poster)
|
||||||
when type in @supported_activities do
|
when type in @supported_activities do
|
||||||
|
feed = ActivityRepresenter.to_simple_form(activity, user, true)
|
||||||
|
|
||||||
|
if feed do
|
||||||
feed =
|
feed =
|
||||||
ActivityRepresenter.to_simple_form(activity, user, true)
|
ActivityRepresenter.wrap_with_entry(feed)
|
||||||
|> ActivityRepresenter.wrap_with_entry()
|
|
||||||
|> :xmerl.export_simple(:xmerl_xml)
|
|> :xmerl.export_simple(:xmerl_xml)
|
||||||
|> to_string
|
|> to_string
|
||||||
|
|
||||||
if feed do
|
|
||||||
{:ok, private, _} = keys_from_pem(keys)
|
{:ok, private, _} = keys_from_pem(keys)
|
||||||
{:ok, feed} = encode(private, feed)
|
{:ok, feed} = encode(private, feed)
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
||||||
alias Pleroma.Web
|
alias Pleroma.Web
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Web.WebFinger
|
alias Pleroma.Web.WebFinger
|
||||||
|
alias Pleroma.Web.CommonAPI
|
||||||
alias Comeonin.Pbkdf2
|
alias Comeonin.Pbkdf2
|
||||||
alias Pleroma.Formatter
|
alias Pleroma.Formatter
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
@ -195,4 +196,15 @@ def follow_import(%{assigns: %{user: user}} = conn, %{"list" => list}) do
|
||||||
|
|
||||||
json(conn, "job started")
|
json(conn, "job started")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete_account(%{assigns: %{user: user}} = conn, params) do
|
||||||
|
case CommonAPI.Utils.confirm_current_password(user, params) do
|
||||||
|
{:ok, user} ->
|
||||||
|
Task.start(fn -> User.delete(user) end)
|
||||||
|
json(conn, %{status: "success"})
|
||||||
|
|
||||||
|
{:error, msg} ->
|
||||||
|
json(conn, %{error: msg})
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -197,7 +197,8 @@ def to_map(
|
||||||
"external_url" => object["external_url"] || object["id"],
|
"external_url" => object["external_url"] || object["id"],
|
||||||
"tags" => tags,
|
"tags" => tags,
|
||||||
"activity_type" => "post",
|
"activity_type" => "post",
|
||||||
"possibly_sensitive" => possibly_sensitive
|
"possibly_sensitive" => possibly_sensitive,
|
||||||
|
"visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,18 @@ def create_status(%User{} = user, %{"status" => _} = data) do
|
||||||
CommonAPI.post(user, data)
|
CommonAPI.post(user, data)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def delete(%User{} = user, id) do
|
||||||
|
# TwitterAPI does not have an "unretweet" endpoint; instead this is done
|
||||||
|
# via the "destroy" endpoint. Therefore, we need to handle
|
||||||
|
# when the status to "delete" is actually an Announce (repeat) object.
|
||||||
|
with %Activity{data: %{"type" => type}} <- Repo.get(Activity, id) do
|
||||||
|
case type do
|
||||||
|
"Announce" -> unrepeat(user, id)
|
||||||
|
_ -> CommonAPI.delete(id, user)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def follow(%User{} = follower, params) do
|
def follow(%User{} = follower, params) do
|
||||||
with {:ok, %User{} = followed} <- get_user(params),
|
with {:ok, %User{} = followed} <- get_user(params),
|
||||||
{:ok, follower} <- User.follow(follower, followed),
|
{:ok, follower} <- User.follow(follower, followed),
|
||||||
|
@ -63,15 +75,21 @@ def repeat(%User{} = user, ap_id_or_id) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp unrepeat(%User{} = user, ap_id_or_id) do
|
||||||
|
with {:ok, _unannounce, activity, _object} <- CommonAPI.unrepeat(ap_id_or_id, user) do
|
||||||
|
{:ok, activity}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def fav(%User{} = user, ap_id_or_id) do
|
def fav(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, _announce, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
with {:ok, _fav, %{data: %{"id" => id}}} = CommonAPI.favorite(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def unfav(%User{} = user, ap_id_or_id) do
|
def unfav(%User{} = user, ap_id_or_id) do
|
||||||
with {:ok, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
with {:ok, _unfav, _fav, %{data: %{"id" => id}}} = CommonAPI.unfavorite(ap_id_or_id, user),
|
||||||
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
%Activity{} = activity <- Activity.get_create_activity_by_object_ap_id(id) do
|
||||||
{:ok, activity}
|
{:ok, activity}
|
||||||
end
|
end
|
||||||
|
|
|
@ -96,13 +96,7 @@ def show_user(conn, params) do
|
||||||
def user_timeline(%{assigns: %{user: user}} = conn, params) do
|
def user_timeline(%{assigns: %{user: user}} = conn, params) do
|
||||||
case TwitterAPI.get_user(user, params) do
|
case TwitterAPI.get_user(user, params) do
|
||||||
{:ok, target_user} ->
|
{:ok, target_user} ->
|
||||||
params =
|
activities = ActivityPub.fetch_user_activities(target_user, user, params)
|
||||||
params
|
|
||||||
|> Map.put("type", ["Create", "Announce"])
|
|
||||||
|> Map.put("actor_id", target_user.ap_id)
|
|
||||||
|> Map.put("whole_db", true)
|
|
||||||
|
|
||||||
activities = ActivityPub.fetch_public_activities(params)
|
|
||||||
|
|
||||||
conn
|
conn
|
||||||
|> render(ActivityView, "index.json", %{activities: activities, for: user})
|
|> render(ActivityView, "index.json", %{activities: activities, for: user})
|
||||||
|
@ -157,8 +151,8 @@ def unblock(%{assigns: %{user: user}} = conn, params) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
def delete_post(%{assigns: %{user: user}} = conn, %{"id" => id}) do
|
||||||
with {:ok, delete} <- CommonAPI.delete(id, user) do
|
with {:ok, activity} <- TwitterAPI.delete(user, id) do
|
||||||
render(conn, ActivityView, "activity.json", %{activity: delete, for: user})
|
render(conn, ActivityView, "activity.json", %{activity: activity, for: user})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -262,7 +262,8 @@ def render(
|
||||||
"external_url" => object["external_url"] || object["id"],
|
"external_url" => object["external_url"] || object["id"],
|
||||||
"tags" => tags,
|
"tags" => tags,
|
||||||
"activity_type" => "post",
|
"activity_type" => "post",
|
||||||
"possibly_sensitive" => possibly_sensitive
|
"possibly_sensitive" => possibly_sensitive,
|
||||||
|
"visibility" => Pleroma.Web.MastodonAPI.StatusView.get_visibility(object)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -86,6 +86,11 @@ def represent_user(user, "JSON") do
|
||||||
"href" => "data:application/magic-public-key,#{magic_key}"
|
"href" => "data:application/magic-public-key,#{magic_key}"
|
||||||
},
|
},
|
||||||
%{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id},
|
%{"rel" => "self", "type" => "application/activity+json", "href" => user.ap_id},
|
||||||
|
%{
|
||||||
|
"rel" => "self",
|
||||||
|
"type" => "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
|
||||||
|
"href" => user.ap_id
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
"rel" => "http://ostatus.org/schema/1.0/subscribe",
|
||||||
"template" => OStatus.remote_follow_path()
|
"template" => OStatus.remote_follow_path()
|
||||||
|
@ -183,6 +188,9 @@ defp webfinger_from_json(doc) do
|
||||||
{"application/activity+json", "self"} ->
|
{"application/activity+json", "self"} ->
|
||||||
Map.put(data, "ap_id", link["href"])
|
Map.put(data, "ap_id", link["href"])
|
||||||
|
|
||||||
|
{"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"", "self"} ->
|
||||||
|
Map.put(data, "ap_id", link["href"])
|
||||||
|
|
||||||
{_, "magic-public-key"} ->
|
{_, "magic-public-key"} ->
|
||||||
"data:application/magic-public-key," <> magic_key = link["href"]
|
"data:application/magic-public-key," <> magic_key = link["href"]
|
||||||
Map.put(data, "magic_key", magic_key)
|
Map.put(data, "magic_key", magic_key)
|
||||||
|
@ -206,7 +214,7 @@ defp webfinger_from_json(doc) do
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_template_from_xml(body) do
|
def get_template_from_xml(body) do
|
||||||
xpath = "//Link[@rel='lrdd' and @type='application/xrd+xml']/@template"
|
xpath = "//Link[@rel='lrdd']/@template"
|
||||||
|
|
||||||
with doc when doc != :error <- XML.parse_document(body),
|
with doc when doc != :error <- XML.parse_document(body),
|
||||||
template when template != nil <- XML.string_from_xpath(xpath, doc) do
|
template when template != nil <- XML.string_from_xpath(xpath, doc) do
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.ModifyActivityIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
@disable_ddl_transaction true
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create index(:activities, ["id desc nulls last", "local"], concurrently: true)
|
||||||
|
drop_if_exists index(:activities, ["id desc nulls last"])
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,15 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.AddTrigramExtension do
|
||||||
|
use Ecto.Migration
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def up do
|
||||||
|
Logger.warn("ATTENTION ATTENTION ATTENTION\n")
|
||||||
|
Logger.warn("This will try to create the pg_trgm extension on your database. If your database user does NOT have the necessary rights, you will have to do it manually and re-run the migrations.\nYou can probably do this by running the following:\n")
|
||||||
|
Logger.warn("sudo -u postgres psql pleroma_dev -c \"create extension if not exists pg_trgm\"\n")
|
||||||
|
execute("create extension if not exists pg_trgm")
|
||||||
|
end
|
||||||
|
|
||||||
|
def down do
|
||||||
|
execute("drop extension if exists pg_trgm")
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,7 @@
|
||||||
|
defmodule Pleroma.Repo.Migrations.CreateUserTrigramIndex do
|
||||||
|
use Ecto.Migration
|
||||||
|
|
||||||
|
def change do
|
||||||
|
create index(:users, ["(nickname || name) gist_trgm_ops"], name: :users_trigram_index, using: :gist)
|
||||||
|
end
|
||||||
|
end
|
|
@ -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=icon type=image/png href=/favicon.png><link rel=stylesheet href=/static/font/css/fontello.css><link rel=stylesheet href=/static/font/css/animation.css><link href=/static/css/app.6c8da1b0ace79ad8881a0e5e716ec818.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.38e369a50eccc2857845.js></script><script type=text/javascript src=/static/js/vendor.ef2aee0b2db579c3a86a.js></script><script type=text/javascript src=/static/js/app.af121efa5ff89725b4c6.js></script></body></html>
|
<!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.c0e1e1e1fcff94fd1e14fc44bfee9a1e.css rel=stylesheet></head><body style="display: none"><div id=app></div><script type=text/javascript src=/static/js/manifest.f40706e221610d4c6256.js></script><script type=text/javascript src=/static/js/vendor.56aa9f8c34786f6af6b7.js></script><script type=text/javascript src=/static/js/app.029b23b3921537271958.js></script></body></html>
|
|
@ -5,5 +5,10 @@
|
||||||
"redirectRootNoLogin": "/main/all",
|
"redirectRootNoLogin": "/main/all",
|
||||||
"redirectRootLogin": "/main/friends",
|
"redirectRootLogin": "/main/friends",
|
||||||
"chatDisabled": false,
|
"chatDisabled": false,
|
||||||
|
"showWhoToFollowPanel": false,
|
||||||
|
"whoToFollowProvider": "https://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-osa-api.cgi?{{host}}+{{user}}",
|
||||||
|
"whoToFollowProviderDummy2": "https://followlink.osa-p.net/api/get_recommend.json?acct=@{{user}}@{{host}}",
|
||||||
|
"whoToFollowLink": "https://vinayaka.distsn.org/?{{host}}+{{user}}",
|
||||||
|
"whoToFollowLinkDummy2": "https://followlink.osa-p.net/recommend.html",
|
||||||
"showInstanceSpecificPanel": false
|
"showInstanceSpecificPanel": false
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
priv/static/static/css/app.c0e1e1e1fcff94fd1e14fc44bfee9a1e.css
Normal file
BIN
priv/static/static/css/app.c0e1e1e1fcff94fd1e14fc44bfee9a1e.css
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
BIN
priv/static/static/font/.config.json.swp
Normal file
BIN
priv/static/static/font/.config.json.swp
Normal file
Binary file not shown.
|
@ -155,6 +155,30 @@
|
||||||
"css": "bell",
|
"css": "bell",
|
||||||
"code": 59408,
|
"code": 59408,
|
||||||
"src": "fontawesome"
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "ccc2329632396dc096bb638d4b46fb98",
|
||||||
|
"css": "mail-alt",
|
||||||
|
"code": 61664,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "c1f1975c885aa9f3dad7810c53b82074",
|
||||||
|
"css": "lock",
|
||||||
|
"code": 59409,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "05376be04a27d5a46e855a233d6e8508",
|
||||||
|
"css": "lock-open-alt",
|
||||||
|
"code": 61758,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "197375a3cea8cb90b02d06e4ddf1433d",
|
||||||
|
"css": "globe",
|
||||||
|
"code": 59410,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
BIN
priv/static/static/font/css/fontello-codes.css
vendored
BIN
priv/static/static/font/css/fontello-codes.css
vendored
Binary file not shown.
BIN
priv/static/static/font/css/fontello-embedded.css
vendored
BIN
priv/static/static/font/css/fontello-embedded.css
vendored
Binary file not shown.
BIN
priv/static/static/font/css/fontello-ie7-codes.css
vendored
BIN
priv/static/static/font/css/fontello-ie7-codes.css
vendored
Binary file not shown.
BIN
priv/static/static/font/css/fontello-ie7.css
vendored
BIN
priv/static/static/font/css/fontello-ie7.css
vendored
Binary file not shown.
BIN
priv/static/static/font/css/fontello.css
vendored
BIN
priv/static/static/font/css/fontello.css
vendored
Binary file not shown.
|
@ -229,11 +229,11 @@ body {
|
||||||
}
|
}
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: 'fontello';
|
font-family: 'fontello';
|
||||||
src: url('./font/fontello.eot?34497073');
|
src: url('./font/fontello.eot?48963108');
|
||||||
src: url('./font/fontello.eot?34497073#iefix') format('embedded-opentype'),
|
src: url('./font/fontello.eot?48963108#iefix') format('embedded-opentype'),
|
||||||
url('./font/fontello.woff?34497073') format('woff'),
|
url('./font/fontello.woff?48963108') format('woff'),
|
||||||
url('./font/fontello.ttf?34497073') format('truetype'),
|
url('./font/fontello.ttf?48963108') format('truetype'),
|
||||||
url('./font/fontello.svg?34497073#fontello') format('svg');
|
url('./font/fontello.svg?48963108#fontello') format('svg');
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
}
|
}
|
||||||
|
@ -323,14 +323,20 @@ body {
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xe810"><i class="demo-icon icon-bell"></i> <span class="i-name">icon-bell</span><span class="i-code">0xe810</span></div>
|
<div class="the-icons span3" title="Code: 0xe810"><i class="demo-icon icon-bell"></i> <span class="i-name">icon-bell</span><span class="i-code">0xe810</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe811"><i class="demo-icon icon-lock"></i> <span class="i-name">icon-lock</span><span class="i-code">0xe811</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe812"><i class="demo-icon icon-globe"></i> <span class="i-name">icon-globe</span><span class="i-code">0xe812</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></i> <span class="i-name">icon-spin3</span><span class="i-code">0xe832</span></div>
|
<div class="the-icons span3" title="Code: 0xe832"><i class="demo-icon icon-spin3 animate-spin"></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"></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"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe834"><i class="demo-icon icon-spin4 animate-spin"></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"></i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf0c9"><i class="demo-icon icon-menu"></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"></i> <span class="i-name">icon-menu</span><span class="i-code">0xf0c9</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf0e0"><i class="demo-icon icon-mail-alt"></i> <span class="i-name">icon-mail-alt</span><span class="i-code">0xf0e0</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></i> <span class="i-name">icon-comment-empty</span><span class="i-code">0xf0e5</span></div>
|
<div class="the-icons span3" title="Code: 0xf0e5"><i class="demo-icon icon-comment-empty"></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"></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"></i> <span class="i-name">icon-reply</span><span class="i-code">0xf112</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xf13e"><i class="demo-icon icon-lock-open-alt"></i> <span class="i-name">icon-lock-open-alt</span><span class="i-code">0xf13e</span></div>
|
||||||
<div class="the-icons span3" title="Code: 0xf1e5"><i class="demo-icon icon-binoculars"></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"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
Binary file not shown.
|
@ -40,6 +40,10 @@
|
||||||
|
|
||||||
<glyph glyph-name="bell" unicode="" 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="bell" unicode="" 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="lock" unicode="" d="M179 421h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
|
||||||
|
|
||||||
|
<glyph glyph-name="globe" unicode="" d="M429 779q116 0 215-58t156-156 57-215-57-215-156-156-215-58-216 58-155 156-58 215 58 215 155 156 216 58z m153-291q-2-1-6-5t-7-6q1 0 2 3t3 6 2 4q3 4 12 8 8 4 29 7 19 5 29-6-1 1 5 7t8 7q2 1 8 3t9 4l1 12q-7-1-10 4t-3 12q0-2-4-5 0 4-2 5t-7-1-5-1q-5 2-8 5t-5 9-2 8q-1 3-5 6t-5 6q-1 1-2 3t-1 4-3 3-3 1-4-3-4-5-2-3q-2 1-4 1t-2-1-3-1-3-2q-1-2-4-2t-5-1q8 3-1 6-5 2-9 2 6 2 5 6t-5 8h3q-1 2-5 5t-10 5-7 3q-5 3-19 5t-18 1q-3-4-3-6t2-8 2-7q1-3-3-7t-3-7q0-4 7-9t6-12q-2-4-9-9t-9-6q-3-5-1-11t6-9q1-1 1-2t-2-3-3-2-4-2l-1-1q-7-3-12 3t-7 15q-4 14-9 17-13 4-16-1-3 7-23 15-14 5-33 2 4 0 0 8-4 9-10 7 1 3 2 10t0 7q2 8 7 13 1 1 4 5t5 7 1 4q19-3 28 6 2 3 6 9t6 10q5 3 8 3t8-3 8-3q8-1 8 6t-4 11q7 0 2 10-2 4-5 5-6 2-15-3-4-2 2-4-1 0-6-6t-9-10-9 3q0 0-3 7t-5 8q-5 0-9-9 1 5-6 9t-14 4q11 7-4 15-4 3-12 3t-11-2q-2-4-3-7t3-4 6-3 6-2 5-2q8-6 5-8-1 0-5-2t-6-2-4-2q-1-3 0-8t-1-8q-3 3-5 10t-4 9q4-5-14-3l-5 0q-3 0-9-1t-12-1-7 5q-3 4 0 11 0 2 2 1-2 2-6 5t-6 5q-25-8-52-23 3 0 6 1 3 1 8 4t5 3q19 7 24 4l3 2q7-9 11-14-4 3-17 1-11-3-12-7 4-6 2-10-2 2-6 6t-8 6-8 3q-9 0-13-1-81-45-131-124 4-4 7-4 2-1 3-5t1-6 6 1q5-4 2-10 1 0 25-15 10-10 11-12 2-6-5-10-1 1-5 5t-5 2q-2-3 0-10t6-7q-4 0-5-9t-2-20 0-13l1-1q-2-6 3-19t12-11q-7-1 11-24 3-4 4-5 2-1 7-4t9-6 5-5q2-3 6-13t8-13q-2-3 5-11t6-13q-1 0-2-1t-1 0q2-4 9-8t8-7q1-2 1-6t2-6 4-1q2 11-13 35-8 13-9 16-2 2-4 8t-2 8q1 0 3 0t5-2 4-3 1-1q-1-4 1-10t7-10 10-11 6-7q4-4 8-11t0-8q5 0 11-5t10-11q3-5 4-15t3-13q1-4 5-8t7-5l9-5t7-3q3-2 10-6t12-7q6-2 9-2t8 1 8 2q8 1 16-8t12-12q20-10 30-6-1 0 1-4t4-9 5-8 3-5q3-3 10-8t10-8q4 2 4 5-1-5 4-11t10-6q8 2 8 18-17-8-27 10 0 0-2 3t-2 5-1 4 0 5 2 1q5 0 6 2t-1 7-2 8q-1 4-6 11t-7 8q-3-5-9-4t-9 5q0-1-1-3t-1-4q-7 0-8 0 1 2 1 10t2 13q1 2 3 6t5 9 2 7-3 5-9 1q-11 0-15-11-1-2-2-6t-2-6-5-4q-4-2-14-1t-13 3q-8 4-13 16t-5 20q0 6 1 15t2 14-3 14q2 1 5 5t5 6q2 1 3 1t3 0 2 1 1 3q0 1-2 2-1 1-2 1 4-1 16 1t15-1q9-6 12 1 0 1-1 6t0 7q3-15 16-5 2-1 9-3t9-2q2-1 4-3t3-3 3 0 5 4q5-8 7-13 6-23 10-25 4-2 6-1t3 5 0 8-1 7l-1 5v10l0 4q-8 2-10 7t0 10 9 10q0 1 4 2t9 4 7 4q12 11 8 20 4 0 6 5 0 0-2 2t-5 2-2 2q5 2 1 8 3 2 4 7t4 5q5-6 12-1 5 5 1 9 2 4 11 6t10 5q4-1 5 1t0 7 2 7q2 2 9 5t7 2l9 7q2 2 0 2 10-1 18 6 5 6-4 11 2 4-1 5t-9 4q2 0 7 0t5 1q9 5-3 9-10 2-24-7z m-91-490q115 21 195 106-1 2-7 2t-7 2q-10 4-13 5 1 4-1 7t-5 5-7 5-6 4q-1 1-4 3t-4 3-4 2-5 2-5-1l-2-1q-2 0-3-1t-3-2-2-1 0-2q-12 10-20 13-3 0-6 3t-6 4-6 0-6-3q-3-3-4-9t-1-7q-4 3 0 10t1 10q-1 3-6 2t-6-2-7-5-5-3-4-3-5-5q-2-2-4-6t-2-6q-1 2-7 3t-5 3q1-5 2-19t3-22q4-17-7-26-15-14-16-23-2-12 7-14 0-4-5-12t-4-12q0-3 2-9z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
<glyph glyph-name="spin3" unicode="" 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="" 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="" 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="" 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" />
|
||||||
|
@ -48,10 +52,14 @@
|
||||||
|
|
||||||
<glyph glyph-name="menu" unicode="" 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="" 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="mail-alt" unicode="" d="M1000 454v-443q0-37-26-63t-63-27h-822q-36 0-63 27t-26 63v443q25-27 56-49 202-137 278-192 32-24 51-37t53-27 61-13h2q28 0 61 13t53 27 51 37q95 68 278 192 32 22 56 49z m0 164q0-44-27-84t-68-69q-210-146-262-181-5-4-23-17t-30-22-29-18-32-15-28-5h-2q-12 0-27 5t-32 15-30 18-30 22-23 17q-51 35-147 101t-114 80q-35 23-65 64t-31 77q0 43 23 72t66 29h822q36 0 63-26t26-63z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
<glyph glyph-name="comment-empty" unicode="" 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="comment-empty" unicode="" 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="" 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="" 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="lock-open-alt" unicode="" d="M589 421q23 0 38-15t16-38v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v179q0 103 74 177t176 73 177-73 73-177q0-14-10-25t-25-11h-36q-14 0-25 11t-11 25q0 59-42 101t-101 42-101-42-41-101v-179h410z" horiz-adv-x="642.9" />
|
||||||
|
|
||||||
<glyph glyph-name="binoculars" unicode="" 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="" 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="user-plus" unicode="" d="M393 350q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
|
<glyph glyph-name="user-plus" unicode="" d="M393 350q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
|
||||||
|
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 15 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
30
priv/static/static/fontold/LICENSE.txt
Normal file
30
priv/static/static/fontold/LICENSE.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
Font license info
|
||||||
|
|
||||||
|
|
||||||
|
## Font Awesome
|
||||||
|
|
||||||
|
Copyright (C) 2016 by Dave Gandy
|
||||||
|
|
||||||
|
Author: Dave Gandy
|
||||||
|
License: SIL ()
|
||||||
|
Homepage: http://fortawesome.github.com/Font-Awesome/
|
||||||
|
|
||||||
|
|
||||||
|
## Entypo
|
||||||
|
|
||||||
|
Copyright (C) 2012 by Daniel Bruce
|
||||||
|
|
||||||
|
Author: Daniel Bruce
|
||||||
|
License: SIL (http://scripts.sil.org/OFL)
|
||||||
|
Homepage: http://www.entypo.com
|
||||||
|
|
||||||
|
|
||||||
|
## Fontelico
|
||||||
|
|
||||||
|
Copyright (C) 2012 by Fontello project
|
||||||
|
|
||||||
|
Author: Crowdsourced, for Fontello project
|
||||||
|
License: SIL (http://scripts.sil.org/OFL)
|
||||||
|
Homepage: http://fontello.com
|
||||||
|
|
||||||
|
|
75
priv/static/static/fontold/README.txt
Normal file
75
priv/static/static/fontold/README.txt
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
This webfont is generated by http://fontello.com open source project.
|
||||||
|
|
||||||
|
|
||||||
|
================================================================================
|
||||||
|
Please, note, that you should obey original font licenses, used to make this
|
||||||
|
webfont pack. Details available in LICENSE.txt file.
|
||||||
|
|
||||||
|
- Usually, it's enough to publish content of LICENSE.txt file somewhere on your
|
||||||
|
site in "About" section.
|
||||||
|
|
||||||
|
- If your project is open-source, usually, it will be ok to make LICENSE.txt
|
||||||
|
file publicly available in your repository.
|
||||||
|
|
||||||
|
- Fonts, used in Fontello, don't require a clickable link on your site.
|
||||||
|
But any kind of additional authors crediting is welcome.
|
||||||
|
================================================================================
|
||||||
|
|
||||||
|
|
||||||
|
Comments on archive content
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
- /font/* - fonts in different formats
|
||||||
|
|
||||||
|
- /css/* - different kinds of css, for all situations. Should be ok with
|
||||||
|
twitter bootstrap. Also, you can skip <i> style and assign icon classes
|
||||||
|
directly to text elements, if you don't mind about IE7.
|
||||||
|
|
||||||
|
- demo.html - demo file, to show your webfont content
|
||||||
|
|
||||||
|
- LICENSE.txt - license info about source fonts, used to build your one.
|
||||||
|
|
||||||
|
- config.json - keeps your settings. You can import it back into fontello
|
||||||
|
anytime, to continue your work
|
||||||
|
|
||||||
|
|
||||||
|
Why so many CSS files ?
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Because we like to fit all your needs :)
|
||||||
|
|
||||||
|
- basic file, <your_font_name>.css - is usually enough, it contains @font-face
|
||||||
|
and character code definitions
|
||||||
|
|
||||||
|
- *-ie7.css - if you need IE7 support, but still don't wish to put char codes
|
||||||
|
directly into html
|
||||||
|
|
||||||
|
- *-codes.css and *-ie7-codes.css - if you like to use your own @font-face
|
||||||
|
rules, but still wish to benefit from css generation. That can be very
|
||||||
|
convenient for automated asset build systems. When you need to update font -
|
||||||
|
no need to manually edit files, just override old version with archive
|
||||||
|
content. See fontello source code for examples.
|
||||||
|
|
||||||
|
- *-embedded.css - basic css file, but with embedded WOFF font, to avoid
|
||||||
|
CORS issues in Firefox and IE9+, when fonts are hosted on the separate domain.
|
||||||
|
We strongly recommend to resolve this issue by `Access-Control-Allow-Origin`
|
||||||
|
server headers. But if you ok with dirty hack - this file is for you. Note,
|
||||||
|
that data url moved to separate @font-face to avoid problems with <IE9, when
|
||||||
|
string is too long.
|
||||||
|
|
||||||
|
- animate.css - use it to get ideas about spinner rotation animation.
|
||||||
|
|
||||||
|
|
||||||
|
Attention for server setup
|
||||||
|
--------------------------
|
||||||
|
|
||||||
|
You MUST setup server to reply with proper `mime-types` for font files -
|
||||||
|
otherwise some browsers will fail to show fonts.
|
||||||
|
|
||||||
|
Usually, `apache` already has necessary settings, but `nginx` and other
|
||||||
|
webservers should be tuned. Here is list of mime types for our file extensions:
|
||||||
|
|
||||||
|
- `application/vnd.ms-fontobject` - eot
|
||||||
|
- `application/x-font-woff` - woff
|
||||||
|
- `application/x-font-ttf` - ttf
|
||||||
|
- `image/svg+xml` - svg
|
160
priv/static/static/fontold/config.json
Normal file
160
priv/static/static/fontold/config.json
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
{
|
||||||
|
"name": "",
|
||||||
|
"css_prefix_text": "icon-",
|
||||||
|
"css_use_suffix": false,
|
||||||
|
"hinting": true,
|
||||||
|
"units_per_em": 1000,
|
||||||
|
"ascent": 850,
|
||||||
|
"glyphs": [
|
||||||
|
{
|
||||||
|
"uid": "9bd60140934a1eb9236fd7a8ab1ff6ba",
|
||||||
|
"css": "spin4",
|
||||||
|
"code": 59444,
|
||||||
|
"src": "fontelico"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "5211af474d3a9848f67f945e2ccaf143",
|
||||||
|
"css": "cancel",
|
||||||
|
"code": 59392,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "eeec3208c90b7b48e804919d0d2d4a41",
|
||||||
|
"css": "upload",
|
||||||
|
"code": 59393,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "2a6740fc2f9d0edea54205963f662594",
|
||||||
|
"css": "spin3",
|
||||||
|
"code": 59442,
|
||||||
|
"src": "fontelico"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "c6be5a58ee4e63a5ec399c2b0d15cf2c",
|
||||||
|
"css": "reply",
|
||||||
|
"code": 61714,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "474656633f79ea2f1dad59ff63f6bf07",
|
||||||
|
"css": "star",
|
||||||
|
"code": 59394,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "d17030afaecc1e1c22349b99f3c4992a",
|
||||||
|
"css": "star-empty",
|
||||||
|
"code": 59395,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "09feb4465d9bd1364f4e301c9ddbaa92",
|
||||||
|
"css": "retweet",
|
||||||
|
"code": 59396,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "7fd683b2c518ceb9e5fa6757f2276faa",
|
||||||
|
"css": "eye-off",
|
||||||
|
"code": 59397,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "73ffeb70554099177620847206c12457",
|
||||||
|
"css": "binoculars",
|
||||||
|
"code": 61925,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "9e1c33b6849ceb08db8acfaf02188b7d",
|
||||||
|
"css": "plus-squared",
|
||||||
|
"code": 59398,
|
||||||
|
"src": "entypo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "e99461abfef3923546da8d745372c995",
|
||||||
|
"css": "cog",
|
||||||
|
"code": 59399,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "1bafeeb1808a5fe24484c7890096901a",
|
||||||
|
"css": "user-plus",
|
||||||
|
"code": 62004,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "559647a6f430b3aeadbecd67194451dd",
|
||||||
|
"css": "menu",
|
||||||
|
"code": 61641,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "0d20938846444af8deb1920dc85a29fb",
|
||||||
|
"css": "logout",
|
||||||
|
"code": 59400,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "ccddff8e8670dcd130e3cb55fdfc2fd0",
|
||||||
|
"css": "down-open",
|
||||||
|
"code": 59401,
|
||||||
|
"src": "fontawesome"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "44b9e75612c5fad5505edd70d071651f",
|
||||||
|
"css": "attach",
|
||||||
|
"code": 59402,
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
BIN
priv/static/static/fontold/css/animation.css
Normal file
BIN
priv/static/static/fontold/css/animation.css
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/css/fontello-codes.css
vendored
Normal file
BIN
priv/static/static/fontold/css/fontello-codes.css
vendored
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/css/fontello-embedded.css
vendored
Normal file
BIN
priv/static/static/fontold/css/fontello-embedded.css
vendored
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/css/fontello-ie7-codes.css
vendored
Normal file
BIN
priv/static/static/fontold/css/fontello-ie7-codes.css
vendored
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/css/fontello-ie7.css
vendored
Normal file
BIN
priv/static/static/fontold/css/fontello-ie7.css
vendored
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/css/fontello.css
vendored
Normal file
BIN
priv/static/static/fontold/css/fontello.css
vendored
Normal file
Binary file not shown.
342
priv/static/static/fontold/demo.html
Normal file
342
priv/static/static/fontold/demo.html
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><!--[if lt IE 9]><script language="javascript" type="text/javascript" src="//html5shim.googlecode.com/svn/trunk/html5.js"></script><![endif]-->
|
||||||
|
<meta charset="UTF-8"><style>/*
|
||||||
|
* Bootstrap v2.2.1
|
||||||
|
*
|
||||||
|
* Copyright 2012 Twitter, Inc
|
||||||
|
* Licensed under the Apache License v2.0
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Designed and built with all the love in the world @twitter by @mdo and @fat.
|
||||||
|
*/
|
||||||
|
.clearfix {
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
.clearfix:before,
|
||||||
|
.clearfix:after {
|
||||||
|
display: table;
|
||||||
|
content: "";
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.clearfix:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
html {
|
||||||
|
font-size: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
}
|
||||||
|
a:focus {
|
||||||
|
outline: thin dotted #333;
|
||||||
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
a:hover,
|
||||||
|
a:active {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
button,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
button,
|
||||||
|
input {
|
||||||
|
*overflow: visible;
|
||||||
|
line-height: normal;
|
||||||
|
}
|
||||||
|
button::-moz-focus-inner,
|
||||||
|
input::-moz-focus-inner {
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
color: #333;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: #08c;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
a:hover {
|
||||||
|
color: #005580;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.row {
|
||||||
|
margin-left: -20px;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
.row:before,
|
||||||
|
.row:after {
|
||||||
|
display: table;
|
||||||
|
content: "";
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.row:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
[class*="span"] {
|
||||||
|
float: left;
|
||||||
|
min-height: 1px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.container,
|
||||||
|
.navbar-static-top .container,
|
||||||
|
.navbar-fixed-top .container,
|
||||||
|
.navbar-fixed-bottom .container {
|
||||||
|
width: 940px;
|
||||||
|
}
|
||||||
|
.span12 {
|
||||||
|
width: 940px;
|
||||||
|
}
|
||||||
|
.span11 {
|
||||||
|
width: 860px;
|
||||||
|
}
|
||||||
|
.span10 {
|
||||||
|
width: 780px;
|
||||||
|
}
|
||||||
|
.span9 {
|
||||||
|
width: 700px;
|
||||||
|
}
|
||||||
|
.span8 {
|
||||||
|
width: 620px;
|
||||||
|
}
|
||||||
|
.span7 {
|
||||||
|
width: 540px;
|
||||||
|
}
|
||||||
|
.span6 {
|
||||||
|
width: 460px;
|
||||||
|
}
|
||||||
|
.span5 {
|
||||||
|
width: 380px;
|
||||||
|
}
|
||||||
|
.span4 {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
.span3 {
|
||||||
|
width: 220px;
|
||||||
|
}
|
||||||
|
.span2 {
|
||||||
|
width: 140px;
|
||||||
|
}
|
||||||
|
.span1 {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
[class*="span"].pull-right,
|
||||||
|
.row-fluid [class*="span"].pull-right {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
margin-right: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
*zoom: 1;
|
||||||
|
}
|
||||||
|
.container:before,
|
||||||
|
.container:after {
|
||||||
|
display: table;
|
||||||
|
content: "";
|
||||||
|
line-height: 0;
|
||||||
|
}
|
||||||
|
.container:after {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0 0 10px;
|
||||||
|
}
|
||||||
|
.lead {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
font-size: 21px;
|
||||||
|
font-weight: 200;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
small {
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-family: inherit;
|
||||||
|
font-weight: bold;
|
||||||
|
line-height: 20px;
|
||||||
|
color: inherit;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
}
|
||||||
|
h1 small {
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 1;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
line-height: 40px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 38.5px;
|
||||||
|
}
|
||||||
|
h1 small {
|
||||||
|
font-size: 24.5px;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin-top: 90px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -480px;
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
padding-top: 10px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.footer {
|
||||||
|
color: #ddd;
|
||||||
|
font-size: 12px;
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.footer a {
|
||||||
|
color: #ccc;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.the-icons {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
.switch {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
bottom: 10px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.switch input {
|
||||||
|
margin-right: 0.3em;
|
||||||
|
}
|
||||||
|
.codesOn .i-name {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.codesOn .i-code {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
.i-code {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'fontello';
|
||||||
|
src: url('./font/fontello.eot?34497073');
|
||||||
|
src: url('./font/fontello.eot?34497073#iefix') format('embedded-opentype'),
|
||||||
|
url('./font/fontello.woff?34497073') format('woff'),
|
||||||
|
url('./font/fontello.ttf?34497073') format('truetype'),
|
||||||
|
url('./font/fontello.svg?34497073#fontello') format('svg');
|
||||||
|
font-weight: normal;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.demo-icon
|
||||||
|
{
|
||||||
|
font-family: "fontello";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
speak: none;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
text-decoration: inherit;
|
||||||
|
width: 1em;
|
||||||
|
margin-right: .2em;
|
||||||
|
text-align: center;
|
||||||
|
/* opacity: .8; */
|
||||||
|
|
||||||
|
/* For safety - reset parent styles, that can break glyph codes*/
|
||||||
|
font-variant: normal;
|
||||||
|
text-transform: none;
|
||||||
|
|
||||||
|
/* fix buttons height, for twitter bootstrap */
|
||||||
|
line-height: 1em;
|
||||||
|
|
||||||
|
/* Animation center compensation - margins should be symmetric */
|
||||||
|
/* remove if not needed */
|
||||||
|
margin-left: .2em;
|
||||||
|
|
||||||
|
/* You can be more comfortable with increased icons size */
|
||||||
|
/* font-size: 120%; */
|
||||||
|
|
||||||
|
/* Font smoothing. That was taken from TWBS */
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
|
||||||
|
/* Uncomment for 3D effect */
|
||||||
|
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<link rel="stylesheet" href="css/animation.css"><!--[if IE 7]><link rel="stylesheet" href="css/" + font.fontname + "-ie7.css"><![endif]-->
|
||||||
|
<script>
|
||||||
|
function toggleCodes(on) {
|
||||||
|
var obj = document.getElementById('icons');
|
||||||
|
|
||||||
|
if (on) {
|
||||||
|
obj.className += ' codesOn';
|
||||||
|
} else {
|
||||||
|
obj.className = obj.className.replace(' codesOn', '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container header">
|
||||||
|
<h1>fontello <small>font demo</small></h1>
|
||||||
|
<label class="switch">
|
||||||
|
<input type="checkbox" onclick="toggleCodes(this.checked)">show codes
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div class="container" id="icons">
|
||||||
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-cancel"></i> <span class="i-name">icon-cancel</span><span class="i-code">0xe800</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-upload"></i> <span class="i-name">icon-upload</span><span class="i-code">0xe801</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-star"></i> <span class="i-name">icon-star</span><span class="i-code">0xe802</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-star-empty"></i> <span class="i-name">icon-star-empty</span><span class="i-code">0xe803</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-retweet"></i> <span class="i-name">icon-retweet</span><span class="i-code">0xe804</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-eye-off"></i> <span class="i-name">icon-eye-off</span><span class="i-code">0xe805</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-plus-squared"></i> <span class="i-name">icon-plus-squared</span><span class="i-code">0xe806</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-cog"></i> <span class="i-name">icon-cog</span><span class="i-code">0xe807</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-logout"></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"></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"></i> <span class="i-name">icon-attach</span><span class="i-code">0xe80a</span></div>
|
||||||
|
<div class="the-icons span3" title="Code: 0xe80b"><i class="demo-icon icon-picture"></i> <span class="i-name">icon-picture</span><span class="i-code">0xe80b</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xe80c"><i class="demo-icon icon-video"></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"></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"></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"></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"></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"></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"></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"></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"></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"></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"></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"></i> <span class="i-name">icon-binoculars</span><span class="i-code">0xf1e5</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="the-icons span3" title="Code: 0xf234"><i class="demo-icon icon-user-plus"></i> <span class="i-name">icon-user-plus</span><span class="i-code">0xf234</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="container footer">Generated by <a href="http://fontello.com">fontello.com</a></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
BIN
priv/static/static/fontold/font/fontello.eot
Normal file
BIN
priv/static/static/fontold/font/fontello.eot
Normal file
Binary file not shown.
60
priv/static/static/fontold/font/fontello.svg
Normal file
60
priv/static/static/fontold/font/fontello.svg
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
<?xml version="1.0" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<metadata>Copyright (C) 2018 by original authors @ fontello.com</metadata>
|
||||||
|
<defs>
|
||||||
|
<font id="fontello" horiz-adv-x="1000" >
|
||||||
|
<font-face font-family="fontello" font-weight="400" font-stretch="normal" units-per-em="1000" ascent="850" descent="-150" />
|
||||||
|
<missing-glyph horiz-adv-x="1000" />
|
||||||
|
<glyph glyph-name="cancel" unicode="" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
|
||||||
|
|
||||||
|
<glyph glyph-name="upload" unicode="" d="M714 29q0 14-10 25t-25 10-25-10-11-25 11-25 25-11 25 11 10 25z m143 0q0 14-10 25t-26 10-25-10-10-25 10-25 25-11 26 11 10 25z m72 125v-179q0-22-16-38t-38-16h-821q-23 0-38 16t-16 38v179q0 22 16 38t38 15h238q12-31 39-51t62-20h143q34 0 61 20t40 51h238q22 0 38-15t16-38z m-182 361q-9-22-33-22h-143v-250q0-15-10-25t-25-11h-143q-15 0-25 11t-11 25v250h-143q-23 0-33 22-9 22 8 39l250 250q10 10 25 10t25-10l250-250q18-17 8-39z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
|
<glyph glyph-name="star" unicode="" d="M929 489q0-12-15-27l-202-197 48-279q0-4 0-12 0-11-6-19t-17-9q-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
|
<glyph glyph-name="star-empty" unicode="" d="M635 290l170 166-235 34-106 213-105-213-236-34 171-166-41-235 211 111 211-111z m294 199q0-12-15-27l-202-197 48-279q0-4 0-12 0-28-23-28-10 0-22 7l-251 132-250-132q-12-7-23-7-11 0-17 9t-6 19q0 4 1 12l48 279-203 197q-14 15-14 27 0 21 31 26l280 40 126 254q11 23 27 23t28-23l125-254 280-40q32-5 32-26z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
|
<glyph glyph-name="retweet" unicode="" d="M714 11q0-7-5-13t-13-5h-535q-5 0-8 1t-5 4-3 4-2 7 0 6v335h-107q-15 0-25 11t-11 25q0 13 8 23l179 214q11 12 27 12t28-12l178-214q9-10 9-23 0-15-11-25t-25-11h-107v-214h321q9 0 14-6l89-108q4-5 4-11z m357 232q0-13-8-23l-178-214q-12-13-28-13t-27 13l-179 214q-8 10-8 23 0 14 11 25t25 11h107v214h-322q-9 0-14 7l-89 107q-4 5-4 11 0 7 5 12t13 6h536q4 0 7-1t5-4 3-5 2-6 1-7v-334h107q14 0 25-11t10-25z" horiz-adv-x="1071.4" />
|
||||||
|
|
||||||
|
<glyph glyph-name="eye-off" unicode="" d="M310 105l43 79q-48 35-76 88t-27 114q0 67 34 125-128-65-213-197 94-144 239-209z m217 424q0 11-8 19t-19 7q-70 0-120-50t-50-119q0-11 8-19t19-8 19 8 8 19q0 48 34 82t82 34q11 0 19 8t8 19z m202 106q0-4 0-5-59-105-176-316t-176-316l-28-50q-5-9-15-9-7 0-75 39-9 6-9 16 0 7 25 49-80 36-147 96t-117 137q-11 17-11 38t11 39q86 131 212 207t277 76q50 0 100-10l31 54q5 9 15 9 3 0 10-3t18-9 18-10 18-10 10-7q9-5 9-15z m21-249q0-78-44-142t-117-91l157 280q4-25 4-47z m250-72q0-19-11-38-22-36-61-81-84-96-194-149t-234-53l41 74q119 10 219 76t169 171q-65 100-158 164l35 63q53-36 102-85t81-103q11-19 11-39z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="plus-squared" unicode="" d="M700 750q42 0 71-29t29-71l0-600q0-40-29-70t-71-30l-600 0q-40 0-70 30t-30 70l0 600q0 42 30 71t70 29l600 0z m-50-450l0 100-200 0 0 200-100 0 0-200-200 0 0-100 200 0 0-200 100 0 0 200 200 0z" horiz-adv-x="800" />
|
||||||
|
|
||||||
|
<glyph glyph-name="cog" unicode="" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
|
||||||
|
|
||||||
|
<glyph glyph-name="logout" unicode="" d="M357 46q0-2 1-11t0-14-2-14-5-11-12-3h-178q-67 0-114 47t-47 114v392q0 67 47 114t114 47h178q8 0 13-5t5-13q0-2 1-11t0-15-2-13-5-11-12-3h-178q-37 0-63-26t-27-64v-392q0-37 27-63t63-27h174t6 0 7-2 4-3 4-5 1-8z m518 304q0-14-11-25l-303-304q-11-10-25-10t-25 10-11 25v161h-250q-14 0-25 11t-11 25v214q0 15 11 25t25 11h250v161q0 14 11 25t25 10 25-10l303-304q11-10 11-25z" horiz-adv-x="928.6" />
|
||||||
|
|
||||||
|
<glyph glyph-name="down-open" unicode="" d="M939 399l-414-413q-10-11-25-11t-25 11l-414 413q-11 11-11 26t11 25l93 92q10 11 25 11t25-11l296-296 296 296q11 11 25 11t26-11l92-92q11-11 11-25t-11-26z" horiz-adv-x="1000" />
|
||||||
|
|
||||||
|
<glyph glyph-name="attach" unicode="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="" 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="user-plus" unicode="" d="M393 350q-89 0-152 63t-62 151 62 152 152 63 151-63 63-152-63-151-151-63z m536-71h196q7 0 13-6t5-12v-107q0-8-5-13t-13-5h-196v-197q0-7-6-12t-12-6h-107q-8 0-13 6t-5 12v197h-197q-7 0-12 5t-6 13v107q0 7 6 12t12 6h197v196q0 7 5 13t13 5h107q7 0 12-5t6-13v-196z m-411-125q0-29 21-51t50-21h143v-133q-38-28-95-28h-488q-67 0-108 39t-41 106q0 30 2 58t8 61 15 60 24 55 34 45 48 30 62 11q11 0 22-10 44-34 86-51t92-17 92 17 86 51q11 10 22 10 73 0 121-54h-125q-29 0-50-21t-21-50v-107z" horiz-adv-x="1142.9" />
|
||||||
|
</font>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 11 KiB |
BIN
priv/static/static/fontold/font/fontello.ttf
Normal file
BIN
priv/static/static/fontold/font/fontello.ttf
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/font/fontello.woff
Normal file
BIN
priv/static/static/fontold/font/fontello.woff
Normal file
Binary file not shown.
BIN
priv/static/static/fontold/font/fontello.woff2
Normal file
BIN
priv/static/static/fontold/font/fontello.woff2
Normal file
Binary file not shown.
BIN
priv/static/static/img/nsfw.50fd83c.png
Normal file
BIN
priv/static/static/img/nsfw.50fd83c.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
Before Width: | Height: | Size: 18 KiB |
BIN
priv/static/static/js/app.029b23b3921537271958.js
Normal file
BIN
priv/static/static/js/app.029b23b3921537271958.js
Normal file
Binary file not shown.
BIN
priv/static/static/js/app.029b23b3921537271958.js.map
Normal file
BIN
priv/static/static/js/app.029b23b3921537271958.js.map
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/manifest.f40706e221610d4c6256.js
Normal file
BIN
priv/static/static/js/manifest.f40706e221610d4c6256.js
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/js/vendor.56aa9f8c34786f6af6b7.js.map
Normal file
BIN
priv/static/static/js/vendor.56aa9f8c34786f6af6b7.js.map
Normal file
Binary file not shown.
Binary file not shown.
BIN
priv/static/static/temp/fontello-a9f33f16.zip
Normal file
BIN
priv/static/static/temp/fontello-a9f33f16.zip
Normal file
Binary file not shown.
1
test/fixtures/httpoison_mock/kaniini@gerzilla.de.json
vendored
Normal file
1
test/fixtures/httpoison_mock/kaniini@gerzilla.de.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"subject":"acct:kaniini","aliases":["https:\/\/gerzilla.de\/channel\/kaniini","https:\/\/gerzilla.de\/~kaniini","acct:kaniini@gerzilla.de"],"properties":{"http:\/\/webfinger.net\/ns\/name":"kaniini","http:\/\/xmlns.com\/foaf\/0.1\/name":"kaniini","https:\/\/w3id.org\/security\/v1#publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvXCDkQPw+1N8B2CUd5s2\nbYvjHt+t7soMNfUiRy0qGbgW46S45k5lCq1KpbFIX3sgGZ4OWjnXVbvjCJi4kl5M\nfm5DBXzpuu05AmjVl8hqk4GejajiE\/1Nq0uWHPiOSFWispUjCzzCu65V+IsiE5JU\nvcL6WEf\/pYNRq7gYqyT693F7+cO5\/rVv9OScx5UOxbIuU1VXYhdHCqAMDJWadC89\nhePrcD3HOQKl06W2tDxHcWk6QjrdsUQGbNOgK\/QIN9gSxA+rCFEvH5O0HAhI0aXq\ncOB+vysJUFLeQOAqmAKvKS5V6RqE1GqqT0pDWHack4EmQi0gkgVzo+45xoP6wfDl\nWwG88w21LNxGvGHuN4I8mg6cEoApqKQBSOj086UtfDfSlPC1B+PRD2phE5etucHd\nF\/RIWN3SxVzU9BKIiaDm2gwOpvI8QuorQb6HDtZFO5NsSN3PnMnSywPe7kXl\/469\nuQRYXrseqyOVIi6WjhvXkyWVKVE5CBz+S8wXHfKph+9YOyUcJeAVMijp9wrjBlMc\noSzOGu79oM7tpMSq\/Xo6ePJ\/glNOwZR+OKrg92Qp9BGTKDNwGrxuxP\/9KwWtGLNf\nOMTtIkxtC3ubhxL3lBxOd7l+Bmum0UJV2f8ogkCgvTpIz05jMoyU8qWl6kkWNQlY\nDropXWaOfy7Lac+G4qlfSgsCAwEAAQ==\n-----END PUBLIC KEY-----\n","http:\/\/purl.org\/zot\/federation":"zot,activitypub"},"links":[{"rel":"http:\/\/webfinger.net\/rel\/avatar","type":"image\/png","href":"https:\/\/gerzilla.de\/photo\/profile\/l\/281"},{"rel":"http:\/\/microformats.org\/profile\/hcard","type":"text\/html","href":"https:\/\/gerzilla.de\/hcard\/kaniini"},{"rel":"http:\/\/webfinger.net\/rel\/profile-page","href":"https:\/\/gerzilla.de\/profile\/kaniini"},{"rel":"http:\/\/schemas.google.com\/g\/2010#updates-from","type":"application\/atom+xml","href":"https:\/\/gerzilla.de\/ofeed\/kaniini"},{"rel":"http:\/\/webfinger.net\/rel\/blog","href":"https:\/\/gerzilla.de\/channel\/kaniini"},{"rel":"http:\/\/ostatus.org\/schema\/1.0\/subscribe","template":"https:\/\/gerzilla.de\/follow?f=&url={uri}"},{"rel":"http:\/\/purl.org\/zot\/protocol","href":"https:\/\/gerzilla.de\/.well-known\/zot-info?address=kaniini@gerzilla.de"},{"rel":"http:\/\/purl.org\/openwebauth\/v1","type":"application\/x-zot+json","href":"https:\/\/gerzilla.de\/owa"},{"rel":"magic-public-key","href":"data:application\/magic-public-key,RSA.AL1wg5ED8PtTfAdglHebNm2L4x7fre7KDDX1IkctKhm4FuOkuOZOZQqtSqWxSF97IBmeDlo511W74wiYuJJeTH5uQwV86brtOQJo1ZfIapOBno2o4hP9TatLlhz4jkhVorKVIws8wruuVfiLIhOSVL3C-lhH_6WDUau4GKsk-vdxe_nDuf61b_TknMeVDsWyLlNVV2IXRwqgDAyVmnQvPYXj63A9xzkCpdOltrQ8R3FpOkI63bFEBmzToCv0CDfYEsQPqwhRLx-TtBwISNGl6nDgfr8rCVBS3kDgKpgCrykuVekahNRqqk9KQ1h2nJOBJkItIJIFc6PuOcaD-sHw5VsBvPMNtSzcRrxh7jeCPJoOnBKAKaikAUjo9POlLXw30pTwtQfj0Q9qYROXrbnB3Rf0SFjd0sVc1PQSiImg5toMDqbyPELqK0G-hw7WRTuTbEjdz5zJ0ssD3u5F5f-OvbkEWF67HqsjlSIulo4b15MllSlROQgc_kvMFx3yqYfvWDslHCXgFTIo6fcK4wZTHKEszhru_aDO7aTEqv16Onjyf4JTTsGUfjiq4PdkKfQRkygzcBq8bsT__SsFrRizXzjE7SJMbQt7m4cS95QcTne5fgZrptFCVdn_KIJAoL06SM9OYzKMlPKlpepJFjUJWA66KV1mjn8uy2nPhuKpX0oL.AQAB"},{"rel":"self","type":"application\/ld+json; profile=\"https:\/\/www.w3.org\/ns\/activitystreams\"","href":"https:\/\/gerzilla.de\/channel\/kaniini"}]}
|
1
test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json
vendored
Normal file
1
test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1","https://hubzilla.example.org/apschema/v1.2"],"type":"Person","id":"https://hubzilla.example.org/channel/kaniini","preferredUsername":"kaniini","name":"kaniini","icon":{"type":"Image","mediaType":"image/jpeg","url":"https://hubzilla.example.org/photo/profile/l/281","height":300,"width":300},"url":{"type":"Link","mediaType":"text/html","href":"https://hubzilla.example.org/channel/kaniini"},"inbox":"https://hubzilla.example.org/inbox/kaniini","outbox":"https://hubzilla.example.org/outbox/kaniini","followers":"https://hubzilla.example.org/followers/kaniini","following":"https://hubzilla.example.org/following/kaniini","endpoints":{"sharedInbox":"https://hubzilla.example.org/inbox"},"publicKey":{"id":"https://hubzilla.example.org/channel/kaniini/public_key_pem","owner":"https://hubzilla.example.org/channel/kaniini","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvXCDkQPw+1N8B2CUd5s2\nbYvjHt+t7soMNfUiRy0qGbgW46S45k5lCq1KpbFIX3sgGZ4OWjnXVbvjCJi4kl5M\nfm5DBXzpuu05AmjVl8hqk4GejajiE/1Nq0uWHPiOSFWispUjCzzCu65V+IsiE5JU\nvcL6WEf/pYNRq7gYqyT693F7+cO5/rVv9OScx5UOxbIuU1VXYhdHCqAMDJWadC89\nhePrcD3HOQKl06W2tDxHcWk6QjrdsUQGbNOgK/QIN9gSxA+rCFEvH5O0HAhI0aXq\ncOB+vysJUFLeQOAqmAKvKS5V6RqE1GqqT0pDWHack4EmQi0gkgVzo+45xoP6wfDl\nWwG88w21LNxGvGHuN4I8mg6cEoApqKQBSOj086UtfDfSlPC1B+PRD2phE5etucHd\nF/RIWN3SxVzU9BKIiaDm2gwOpvI8QuorQb6HDtZFO5NsSN3PnMnSywPe7kXl/469\nuQRYXrseqyOVIi6WjhvXkyWVKVE5CBz+S8wXHfKph+9YOyUcJeAVMijp9wrjBlMc\noSzOGu79oM7tpMSq/Xo6ePJ/glNOwZR+OKrg92Qp9BGTKDNwGrxuxP/9KwWtGLNf\nOMTtIkxtC3ubhxL3lBxOd7l+Bmum0UJV2f8ogkCgvTpIz05jMoyU8qWl6kkWNQlY\nDropXWaOfy7Lac+G4qlfSgsCAwEAAQ==\n-----END PUBLIC KEY-----\n"},"nomadicLocations":[{"id":"https://hubzilla.example.org/locs/kaniini","type":"nomadicLocation","locationAddress":"acct:kaniini@hubzilla.example.org","locationPrimary":true,"locationDeleted":false}],"signature":{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1"],"type":"RsaSignature2017","nonce":"6b981a2f3bdcffc20252e3b131d4a4569fd2dea9fac543e5196136302f492694","creator":"https://hubzilla.example.org/channel/kaniini/public_key_pem","created":"2018-05-19T08:19:13Z","signatureValue":"ezpT4iCIUzJSeJa/Jsf4EkgbX9enWZG/0eliLXZcvkeCX9mZabaX9LMQRViP2GSlAJBHJu+UqK5LWaoWw9pYkQQHUL+43w2DeBxQicEcPqpT46j6pHuWptfwB8YHTC2/Pb56Y/jseU37j+FW8xVmcGZk4cPqJRLQNojwJlQiFOpBEd4Cel6081W12Pep578+6xBL+h92RJsWznA1gE/NV9dkCqoAoNdiORJg68sVTm0yYxPit2D/DLwXUFeBhC47EZtY3DtAOf7rADGwbquXKug/wtEI47R4p9dJvMWERSVW9O2FmDk8deUjRR3qO1iYGce8O+uMnnBHmuTcToRUHH7mxfMdqjfbcZ9DGBjKtLPSOyVPT9rENeyX8fsksmX0XhfHsNSWkmeDaU5/Au3IY75gDewiGzmzLOpRc6GUnHHro7lMpyMuo3lLZKjNVsFZbx+sXCYwORz5GAMuwIt/iCUdrsQsF5aycqfUAZrFBPguH6DVjbMUqyLvS78sDKiWqgWVhq9VDKse+WuQaJLGBDJNF9APoA6NDMjjIBZfmkGf2mV7ubIYihoOncUjahFqxU5306cNxAcdj2uNcwkgX4BCnBe/L2YsvMHhZrupzDewWWy4fxhktyoZ7VhLSl1I7fMPytjOpb9EIvng4DHGX2t+hKfon2rCGfECPavwiTM="}}
|
2
test/fixtures/httpoison_mock/status.alpicola.com_host_meta
vendored
Normal file
2
test/fixtures/httpoison_mock/status.alpicola.com_host_meta
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0"><hm:Host xmlns:hm="http://host-meta.net/xrd/1.0">status.alpicola.com</hm:Host><Link rel="lrdd" template="http://status.alpicola.com/main/xrd?uri={uri}"><Title>Resource Descriptor</Title></Link></XRD>
|
31
test/fixtures/hubzilla-follow-activity.json
vendored
Normal file
31
test/fixtures/hubzilla-follow-activity.json
vendored
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
{
|
||||||
|
"type": "Follow",
|
||||||
|
"signature": {
|
||||||
|
"type": "RsaSignature2017",
|
||||||
|
"signatureValue": "Kn1/UkAQGJVaXBfWLAHcnwHg8YMAUqlEaBuYLazAG+pz5hqivsyrBmPV186Xzr+B4ZLExA9+SnOoNx/GOz4hBm0kAmukNSILAsUd84tcJ2yT9zc1RKtembK4WiwOw7li0+maeDN0HaB6t+6eTqsCWmtiZpprhXD8V1GGT8yG7X24fQ9oFGn+ng7lasbcCC0988Y1eGqNe7KryxcPuQz57YkDapvtONzk8gyLTkZMV4De93MyRHq6GVjQVIgtiYabQAxrX6Q8C+4P/jQoqdWJHEe+MY5JKyNaT/hMPt2Md1ok9fZQBGHlErk22/zy8bSN19GdG09HmIysBUHRYpBLig==",
|
||||||
|
"creator": "https://hubzilla.example.org/channel/kaniini#main-key",
|
||||||
|
"created": "2018-02-17T13:29:31Z"
|
||||||
|
},
|
||||||
|
"object": "https://localtesting.pleroma.lol/users/lain",
|
||||||
|
"nickname": "lain",
|
||||||
|
"id": "https://hubzilla.example.org/channel/kaniini#follows/2",
|
||||||
|
"actor": {
|
||||||
|
"id": "https://hubzilla.example.org/channel/kaniini"
|
||||||
|
},
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"movedTo": "as:movedTo",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"Emoji": "toot:Emoji"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
47
test/fixtures/mastodon-undo-announce.json
vendored
Normal file
47
test/fixtures/mastodon-undo-announce.json
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{
|
||||||
|
"type": "Undo",
|
||||||
|
"signature": {
|
||||||
|
"type": "RsaSignature2017",
|
||||||
|
"signatureValue": "VU9AmHf3Pus9cWtMG/TOdxr+MRQfPHdTVKBBgFJBXhAlMhxEtcbxsu7zmqBgfIz6u0HpTCi5jRXEMftc228OJf/aBUkr4hyWADgcdmhPQgpibouDLgQf9BmnrPqb2rMbzZyt49GJkQZma8taLh077TTq6OKcnsAAJ1evEKOcRYS4OxBSwh4nI726bOXzZWoNzpTcrnm+llcUEN980sDSAS0uyZdb8AxZdfdG6DJQX4AkUD5qTpfqP/vC1ISirrNphvVhlxjUV9Amr4SYTsLx80vdZe5NjeL5Ir4jTIIQLedpxaDu1M9Q+Jpc0fYByQ2hOwUq8JxEmvHvarKjrq0Oww==",
|
||||||
|
"creator": "http://mastodon.example.org/users/admin#main-key",
|
||||||
|
"created": "2018-05-11T16:23:45Z"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "Announce",
|
||||||
|
"to": [
|
||||||
|
"http://www.w3.org/ns/activitystreams#Public"
|
||||||
|
],
|
||||||
|
"published": "2018-05-11T16:23:37Z",
|
||||||
|
"object": "http://mastodon.example.org/@admin/99541947525187367",
|
||||||
|
"id": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
|
||||||
|
"cc": [
|
||||||
|
"http://mastodon.example.org/users/admin",
|
||||||
|
"http://mastodon.example.org/users/admin/followers"
|
||||||
|
],
|
||||||
|
"atomUri": "http://mastodon.example.org/users/admin/statuses/99542391527669785/activity",
|
||||||
|
"actor": "http://mastodon.example.org/users/admin"
|
||||||
|
},
|
||||||
|
"id": "http://mastodon.example.org/users/admin#announces/100011594053806179/undo",
|
||||||
|
"actor": "http://mastodon.example.org/users/admin",
|
||||||
|
"@context": [
|
||||||
|
"http://www.w3.org/ns/activitystreams",
|
||||||
|
"http://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"movedTo": "as:movedTo",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"focalPoint": {
|
||||||
|
"@id": "toot:focalPoint",
|
||||||
|
"@container": "@list"
|
||||||
|
},
|
||||||
|
"featured": "toot:featured",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"Emoji": "toot:Emoji"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
34
test/fixtures/mastodon-undo-like.json
vendored
Normal file
34
test/fixtures/mastodon-undo-like.json
vendored
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"type": "Undo",
|
||||||
|
"signature": {
|
||||||
|
"type": "RsaSignature2017",
|
||||||
|
"signatureValue": "fdxMfQSMwbC6wP6sh6neS/vM5879K67yQkHTbiT5Npr5wAac0y6+o3Ij+41tN3rL6wfuGTosSBTHOtta6R4GCOOhCaCSLMZKypnp1VltCzLDoyrZELnYQIC8gpUXVmIycZbREk22qWUe/w7DAFaKK4UscBlHDzeDVcA0K3Se5Sluqi9/Zh+ldAnEzj/rSEPDjrtvf5wGNf3fHxbKSRKFt90JvKK6hS+vxKUhlRFDf6/SMETw+EhwJSNW4d10yMUakqUWsFv4Acq5LW7l+HpYMvlYY1FZhNde1+uonnCyuQDyvzkff8zwtEJmAXC4RivO/VVLa17SmqheJZfI8oluVg==",
|
||||||
|
"creator": "http://mastodon.example.org/users/admin#main-key",
|
||||||
|
"created": "2018-05-19T16:36:58Z"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "Like",
|
||||||
|
"object": "http://localtesting.pleroma.lol/objects/eb92579d-3417-42a8-8652-2492c2d4f454",
|
||||||
|
"id": "http://mastodon.example.org/users/admin#likes/2",
|
||||||
|
"actor": "http://mastodon.example.org/users/admin"
|
||||||
|
},
|
||||||
|
"nickname": "lain",
|
||||||
|
"id": "http://mastodon.example.org/users/admin#likes/2/undo",
|
||||||
|
"actor": "http://mastodon.example.org/users/admin",
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
{
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"sensitive": "as:sensitive",
|
||||||
|
"ostatus": "http://ostatus.org#",
|
||||||
|
"movedTo": "as:movedTo",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||||
|
"conversation": "ostatus:conversation",
|
||||||
|
"atomUri": "ostatus:atomUri",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"Emoji": "toot:Emoji"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -78,6 +78,13 @@ test "turning urls into links" do
|
||||||
"<a href=\"https://en.wikipedia.org/wiki/Duff's_device\">https://en.wikipedia.org/wiki/Duff's_device</a>"
|
"<a href=\"https://en.wikipedia.org/wiki/Duff's_device\">https://en.wikipedia.org/wiki/Duff's_device</a>"
|
||||||
|
|
||||||
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
|
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
|
||||||
|
|
||||||
|
text = "https://pleroma.com https://pleroma.com/sucks"
|
||||||
|
|
||||||
|
expected =
|
||||||
|
"<a href=\"https://pleroma.com\">https://pleroma.com</a> <a href=\"https://pleroma.com/sucks\">https://pleroma.com/sucks</a>"
|
||||||
|
|
||||||
|
assert Formatter.add_links({[], text}) |> Formatter.finalize() == expected
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,13 @@ test "it doesn't create a notification for user if the user blocks the activity
|
||||||
|
|
||||||
assert nil == Notification.create_notification(activity, user)
|
assert nil == Notification.create_notification(activity, user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it doesn't create a notification for user if he is the activity author" do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
author = User.get_by_ap_id(activity.data["actor"])
|
||||||
|
|
||||||
|
assert nil == Notification.create_notification(activity, author)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "get notification" do
|
describe "get notification" do
|
||||||
|
|
|
@ -3,6 +3,18 @@ defmodule HTTPoisonMock do
|
||||||
|
|
||||||
def get(url, body \\ [], headers \\ [])
|
def get(url, body \\ [], headers \\ [])
|
||||||
|
|
||||||
|
def get(
|
||||||
|
"http://gerzilla.de/.well-known/webfinger?resource=acct:kaniini@gerzilla.de",
|
||||||
|
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||||
|
follow_redirect: true
|
||||||
|
) do
|
||||||
|
{:ok,
|
||||||
|
%Response{
|
||||||
|
status_code: 200,
|
||||||
|
body: File.read!("test/fixtures/httpoison_mock/kaniini@gerzilla.de.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get(
|
def get(
|
||||||
"http://framatube.org/.well-known/webfinger?resource=acct:framasoft@framatube.org",
|
"http://framatube.org/.well-known/webfinger?resource=acct:framasoft@framatube.org",
|
||||||
[Accept: "application/xrd+xml,application/jrd+json"],
|
[Accept: "application/xrd+xml,application/jrd+json"],
|
||||||
|
@ -519,6 +531,14 @@ def get("http://social.heldscal.la/.well-known/host-meta", [], follow_redirect:
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get("http://status.alpicola.com/.well-known/host-meta", [], follow_redirect: true) do
|
||||||
|
{:ok,
|
||||||
|
%Response{
|
||||||
|
status_code: 200,
|
||||||
|
body: File.read!("test/fixtures/httpoison_mock/status.alpicola.com_host_meta")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("http://macgirvin.com/.well-known/host-meta", [], follow_redirect: true) do
|
def get("http://macgirvin.com/.well-known/host-meta", [], follow_redirect: true) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Response{
|
%Response{
|
||||||
|
@ -628,6 +648,18 @@ def get("http://mastodon.example.org/users/admin", [Accept: "application/activit
|
||||||
}}
|
}}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get(
|
||||||
|
"https://hubzilla.example.org/channel/kaniini",
|
||||||
|
[Accept: "application/activity+json"],
|
||||||
|
_
|
||||||
|
) do
|
||||||
|
{:ok,
|
||||||
|
%Response{
|
||||||
|
status_code: 200,
|
||||||
|
body: File.read!("test/fixtures/httpoison_mock/kaniini@hubzilla.example.org.json")
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
|
||||||
def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do
|
def get("https://masto.quad.moe/users/_HellPie", [Accept: "application/activity+json"], _) do
|
||||||
{:ok,
|
{:ok,
|
||||||
%Response{
|
%Response{
|
||||||
|
|
|
@ -171,6 +171,19 @@ test "doesn't return blocked activities" do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "public fetch activities" do
|
describe "public fetch activities" do
|
||||||
|
test "doesn't retrieve unlisted activities" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, unlisted_activity} =
|
||||||
|
CommonAPI.post(user, %{"status" => "yeah", "visibility" => "unlisted"})
|
||||||
|
|
||||||
|
{:ok, listed_activity} = CommonAPI.post(user, %{"status" => "yeah"})
|
||||||
|
|
||||||
|
[activity] = ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
|
assert activity == listed_activity
|
||||||
|
end
|
||||||
|
|
||||||
test "retrieves public activities" do
|
test "retrieves public activities" do
|
||||||
_activities = ActivityPub.fetch_public_activities()
|
_activities = ActivityPub.fetch_public_activities()
|
||||||
|
|
||||||
|
@ -264,7 +277,7 @@ test "unliking a previously liked object" do
|
||||||
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
{:ok, like_activity, object} = ActivityPub.like(user, object)
|
||||||
assert object.data["like_count"] == 1
|
assert object.data["like_count"] == 1
|
||||||
|
|
||||||
{:ok, object} = ActivityPub.unlike(user, object)
|
{:ok, _, _, object} = ActivityPub.unlike(user, object)
|
||||||
assert object.data["like_count"] == 0
|
assert object.data["like_count"] == 0
|
||||||
|
|
||||||
assert Repo.get(Activity, like_activity.id) == nil
|
assert Repo.get(Activity, like_activity.id) == nil
|
||||||
|
@ -292,6 +305,38 @@ test "adds an announce activity to the db" do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "unannouncing an object" do
|
||||||
|
test "unannouncing a previously announced object" do
|
||||||
|
note_activity = insert(:note_activity)
|
||||||
|
object = Object.get_by_ap_id(note_activity.data["object"]["id"])
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
# Unannouncing an object that is not announced does nothing
|
||||||
|
# {:ok, object} = ActivityPub.unannounce(user, object)
|
||||||
|
# assert object.data["announcement_count"] == 0
|
||||||
|
|
||||||
|
{:ok, announce_activity, object} = ActivityPub.announce(user, object)
|
||||||
|
assert object.data["announcement_count"] == 1
|
||||||
|
|
||||||
|
{:ok, unannounce_activity, activity, object} = ActivityPub.unannounce(user, object)
|
||||||
|
assert object.data["announcement_count"] == 0
|
||||||
|
|
||||||
|
assert activity == announce_activity
|
||||||
|
|
||||||
|
assert unannounce_activity.data["to"] == [
|
||||||
|
User.ap_followers(user),
|
||||||
|
announce_activity.data["actor"]
|
||||||
|
]
|
||||||
|
|
||||||
|
assert unannounce_activity.data["type"] == "Undo"
|
||||||
|
assert unannounce_activity.data["object"] == announce_activity.data
|
||||||
|
assert unannounce_activity.data["actor"] == user.ap_id
|
||||||
|
assert unannounce_activity.data["context"] == announce_activity.data["context"]
|
||||||
|
|
||||||
|
assert Repo.get(Activity, announce_activity.id) == nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "uploading files" do
|
describe "uploading files" do
|
||||||
test "copies the file to the configured folder" do
|
test "copies the file to the configured folder" do
|
||||||
file = %Plug.Upload{
|
file = %Plug.Upload{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
defmodule Pleroma.Web.ActivityPub.TransmogrifierTest do
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.ActivityPub.Utils
|
||||||
alias Pleroma.Web.OStatus
|
alias Pleroma.Web.OStatus
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
@ -118,6 +119,23 @@ test "it works for incoming follow requests" do
|
||||||
assert User.following?(User.get_by_ap_id(data["actor"]), user)
|
assert User.following?(User.get_by_ap_id(data["actor"]), user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works for incoming follow requests from hubzilla" do
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/hubzilla-follow-activity.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", user.ap_id)
|
||||||
|
|> Utils.normalize_params()
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "https://hubzilla.example.org/channel/kaniini"
|
||||||
|
assert data["type"] == "Follow"
|
||||||
|
assert data["id"] == "https://hubzilla.example.org/channel/kaniini#follows/2"
|
||||||
|
assert User.following?(User.get_by_ap_id(data["actor"]), user)
|
||||||
|
end
|
||||||
|
|
||||||
test "it works for incoming likes" do
|
test "it works for incoming likes" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hello"})
|
||||||
|
@ -135,6 +153,43 @@ test "it works for incoming likes" do
|
||||||
assert data["object"] == activity.data["object"]["id"]
|
assert data["object"] == activity.data["object"]["id"]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it returns an error for incoming unlikes wihout a like activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"]["id"])
|
||||||
|
|
||||||
|
assert Transmogrifier.handle_incoming(data) == :error
|
||||||
|
end
|
||||||
|
|
||||||
|
test "it works for incoming unlikes with an existing like activity" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "leave a like pls"})
|
||||||
|
|
||||||
|
like_data =
|
||||||
|
File.read!("test/fixtures/mastodon-like.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"]["id"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: like_data, local: false}} = Transmogrifier.handle_incoming(like_data)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-undo-like.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", like_data)
|
||||||
|
|> Map.put("actor", like_data["actor"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["actor"] == "http://mastodon.example.org/users/admin"
|
||||||
|
assert data["type"] == "Undo"
|
||||||
|
assert data["id"] == "http://mastodon.example.org/users/admin#likes/2/undo"
|
||||||
|
assert data["object"]["id"] == "http://mastodon.example.org/users/admin#likes/2"
|
||||||
|
end
|
||||||
|
|
||||||
test "it works for incoming announces" do
|
test "it works for incoming announces" do
|
||||||
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
data = File.read!("test/fixtures/mastodon-announce.json") |> Poison.decode!()
|
||||||
|
|
||||||
|
@ -232,6 +287,34 @@ test "it works for incoming deletes" do
|
||||||
|
|
||||||
refute Repo.get(Activity, activity.id)
|
refute Repo.get(Activity, activity.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it works for incoming unannounces with an existing notice" do
|
||||||
|
user = insert(:user)
|
||||||
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "hey"})
|
||||||
|
|
||||||
|
announce_data =
|
||||||
|
File.read!("test/fixtures/mastodon-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", activity.data["object"]["id"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: announce_data, local: false}} =
|
||||||
|
Transmogrifier.handle_incoming(announce_data)
|
||||||
|
|
||||||
|
data =
|
||||||
|
File.read!("test/fixtures/mastodon-undo-announce.json")
|
||||||
|
|> Poison.decode!()
|
||||||
|
|> Map.put("object", announce_data)
|
||||||
|
|> Map.put("actor", announce_data["actor"])
|
||||||
|
|
||||||
|
{:ok, %Activity{data: data, local: false}} = Transmogrifier.handle_incoming(data)
|
||||||
|
|
||||||
|
assert data["type"] == "Undo"
|
||||||
|
assert data["object"]["type"] == "Announce"
|
||||||
|
assert data["object"]["object"] == activity.data["object"]["id"]
|
||||||
|
|
||||||
|
assert data["object"]["id"] ==
|
||||||
|
"http://mastodon.example.org/users/admin/statuses/99542391527669785/activity"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "prepare outgoing" do
|
describe "prepare outgoing" do
|
||||||
|
@ -392,4 +475,15 @@ test "it deletes all websub client subscripitions with the user as topic" do
|
||||||
assert Repo.get(WebsubClientSubscription, ws2.id)
|
assert Repo.get(WebsubClientSubscription, ws2.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "actor rewriting" do
|
||||||
|
test "it fixes the actor URL property to be a proper URI" do
|
||||||
|
data = %{
|
||||||
|
"url" => %{"href" => "http://example.com"}
|
||||||
|
}
|
||||||
|
|
||||||
|
rewritten = Transmogrifier.maybe_fix_user_object(data)
|
||||||
|
assert rewritten["url"] == "http://example.com"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
defmodule Pleroma.Web.CommonAPI.UtilsTest do
|
||||||
alias Pleroma.Web.CommonAPI.Utils
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
alias Pleroma.Builders.{UserBuilder}
|
||||||
use Pleroma.DataCase
|
use Pleroma.DataCase
|
||||||
|
|
||||||
test "it adds attachment links to a given text and attachment set" do
|
test "it adds attachment links to a given text and attachment set" do
|
||||||
|
@ -15,4 +16,18 @@ test "it adds attachment links to a given text and attachment set" do
|
||||||
assert res ==
|
assert res ==
|
||||||
"<br><a href=\"#{name}\" class='attachment'>Sakura Mana – Turned on by a Se…</a>"
|
"<br><a href=\"#{name}\" class='attachment'>Sakura Mana – Turned on by a Se…</a>"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "it confirms the password given is the current users password" do
|
||||||
|
test "incorrect password given" do
|
||||||
|
{:ok, user} = UserBuilder.insert()
|
||||||
|
|
||||||
|
assert Utils.confirm_current_password(user, %{"password" => ""}) ==
|
||||||
|
{:error, "Invalid password."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "correct password given" do
|
||||||
|
{:ok, user} = UserBuilder.insert()
|
||||||
|
assert Utils.confirm_current_password(user, %{"password" => "test"}) == {:ok, user}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -332,6 +332,24 @@ test "reblogs and returns the reblogged status", %{conn: conn} do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "unreblogging" do
|
||||||
|
test "unreblogs and returns the unreblogged status", %{conn: conn} do
|
||||||
|
activity = insert(:note_activity)
|
||||||
|
user = insert(:user)
|
||||||
|
|
||||||
|
{:ok, _, _} = CommonAPI.repeat(activity.id, user)
|
||||||
|
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> post("/api/v1/statuses/#{activity.id}/unreblog")
|
||||||
|
|
||||||
|
assert %{"id" => id, "reblogged" => false, "reblogs_count" => 0} = json_response(conn, 200)
|
||||||
|
|
||||||
|
assert to_string(activity.id) == id
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe "favoriting" do
|
describe "favoriting" do
|
||||||
test "favs a status and returns it", %{conn: conn} do
|
test "favs a status and returns it", %{conn: conn} do
|
||||||
activity = insert(:note_activity)
|
activity = insert(:note_activity)
|
||||||
|
@ -370,18 +388,47 @@ test "unfavorites a status and returns it", %{conn: conn} do
|
||||||
|
|
||||||
describe "user timelines" do
|
describe "user timelines" do
|
||||||
test "gets a users statuses", %{conn: conn} do
|
test "gets a users statuses", %{conn: conn} do
|
||||||
_note = insert(:note_activity)
|
user_one = insert(:user)
|
||||||
note_two = insert(:note_activity)
|
user_two = insert(:user)
|
||||||
|
user_three = insert(:user)
|
||||||
|
|
||||||
user = User.get_by_ap_id(note_two.data["actor"])
|
{:ok, user_three} = User.follow(user_three, user_one)
|
||||||
|
|
||||||
conn =
|
{:ok, activity} = CommonAPI.post(user_one, %{"status" => "HI!!!"})
|
||||||
|
|
||||||
|
{:ok, direct_activity} =
|
||||||
|
CommonAPI.post(user_one, %{
|
||||||
|
"status" => "Hi, @#{user_two.nickname}.",
|
||||||
|
"visibility" => "direct"
|
||||||
|
})
|
||||||
|
|
||||||
|
{:ok, private_activity} =
|
||||||
|
CommonAPI.post(user_one, %{"status" => "private", "visibility" => "private"})
|
||||||
|
|
||||||
|
resp =
|
||||||
conn
|
conn
|
||||||
|> get("/api/v1/accounts/#{user.id}/statuses")
|
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|
||||||
|
|
||||||
assert [%{"id" => id}] = json_response(conn, 200)
|
assert [%{"id" => id}] = json_response(resp, 200)
|
||||||
|
assert id == to_string(activity.id)
|
||||||
|
|
||||||
assert id == to_string(note_two.id)
|
resp =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user_two)
|
||||||
|
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|
||||||
|
|
||||||
|
assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
|
||||||
|
assert id_one == to_string(direct_activity.id)
|
||||||
|
assert id_two == to_string(activity.id)
|
||||||
|
|
||||||
|
resp =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user_three)
|
||||||
|
|> get("/api/v1/accounts/#{user_one.id}/statuses")
|
||||||
|
|
||||||
|
assert [%{"id" => id_one}, %{"id" => id_two}] = json_response(resp, 200)
|
||||||
|
assert id_one == to_string(private_activity.id)
|
||||||
|
assert id_two == to_string(activity.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
test "unimplemented pinned statuses feature", %{conn: conn} do
|
test "unimplemented pinned statuses feature", %{conn: conn} do
|
||||||
|
@ -625,16 +672,29 @@ test "unimplemented mutes, follow_requests, blocks, domain blocks" do
|
||||||
|
|
||||||
test "account search", %{conn: conn} do
|
test "account search", %{conn: conn} do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
_user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
user_two = insert(:user, %{nickname: "shp@shitposter.club"})
|
||||||
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
user_three = insert(:user, %{nickname: "shp@heldscal.la", name: "I love 2hu"})
|
||||||
|
|
||||||
conn =
|
results =
|
||||||
|
conn
|
||||||
|
|> assign(:user, user)
|
||||||
|
|> get("/api/v1/accounts/search", %{"q" => "shp"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
|
result_ids = for result <- results, do: result["acct"]
|
||||||
|
|
||||||
|
assert user_two.nickname in result_ids
|
||||||
|
assert user_three.nickname in result_ids
|
||||||
|
|
||||||
|
results =
|
||||||
conn
|
conn
|
||||||
|> assign(:user, user)
|
|> assign(:user, user)
|
||||||
|> get("/api/v1/accounts/search", %{"q" => "2hu"})
|
|> get("/api/v1/accounts/search", %{"q" => "2hu"})
|
||||||
|
|> json_response(200)
|
||||||
|
|
||||||
assert [account] = json_response(conn, 200)
|
result_ids = for result <- results, do: result["acct"]
|
||||||
assert account["id"] == to_string(user_three.id)
|
|
||||||
|
assert user_three.nickname in result_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
test "search", %{conn: conn} do
|
test "search", %{conn: conn} do
|
||||||
|
@ -658,7 +718,7 @@ test "search", %{conn: conn} do
|
||||||
|
|
||||||
assert results = json_response(conn, 200)
|
assert results = json_response(conn, 200)
|
||||||
|
|
||||||
[account] = results["accounts"]
|
[account | _] = results["accounts"]
|
||||||
assert account["id"] == to_string(user_three.id)
|
assert account["id"] == to_string(user_three.id)
|
||||||
|
|
||||||
assert results["hashtags"] == []
|
assert results["hashtags"] == []
|
||||||
|
|
|
@ -154,7 +154,8 @@ test "an activity" do
|
||||||
"tags" => ["nsfw", "content", "mentioning"],
|
"tags" => ["nsfw", "content", "mentioning"],
|
||||||
"activity_type" => "post",
|
"activity_type" => "post",
|
||||||
"possibly_sensitive" => true,
|
"possibly_sensitive" => true,
|
||||||
"uri" => activity.data["object"]["id"]
|
"uri" => activity.data["object"]["id"],
|
||||||
|
"visibility" => "direct"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert ActivityRepresenter.to_map(activity, %{
|
assert ActivityRepresenter.to_map(activity, %{
|
||||||
|
|
|
@ -257,8 +257,10 @@ test "without valid credentials", %{conn: conn} do
|
||||||
end
|
end
|
||||||
|
|
||||||
test "with credentials", %{conn: conn, user: current_user} do
|
test "with credentials", %{conn: conn, user: current_user} do
|
||||||
|
other_user = insert(:user)
|
||||||
|
|
||||||
{:ok, activity} =
|
{:ok, activity} =
|
||||||
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: current_user})
|
ActivityBuilder.insert(%{"to" => [current_user.ap_id]}, %{user: other_user})
|
||||||
|
|
||||||
conn =
|
conn =
|
||||||
conn
|
conn
|
||||||
|
@ -798,4 +800,31 @@ test "Convert newlines to <br> in bio", %{conn: conn} do
|
||||||
user = Repo.get!(User, user.id)
|
user = Repo.get!(User, user.id)
|
||||||
assert user.bio == "Hello,<br>World! I<br> am a test."
|
assert user.bio == "Hello,<br>World! I<br> am a test."
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "POST /api/pleroma/delete_account" do
|
||||||
|
setup [:valid_user]
|
||||||
|
|
||||||
|
test "without credentials", %{conn: conn} do
|
||||||
|
conn = post(conn, "/api/pleroma/delete_account")
|
||||||
|
assert json_response(conn, 403) == %{"error" => "Invalid credentials."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with credentials and invalid password", %{conn: conn, user: current_user} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> with_credentials(current_user.nickname, "test")
|
||||||
|
|> post("/api/pleroma/delete_account", %{"password" => "hi"})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{"error" => "Invalid password."}
|
||||||
|
end
|
||||||
|
|
||||||
|
test "with credentials and valid password", %{conn: conn, user: current_user} do
|
||||||
|
conn =
|
||||||
|
conn
|
||||||
|
|> with_credentials(current_user.nickname, "test")
|
||||||
|
|> post("/api/pleroma/delete_account", %{"password" => "test"})
|
||||||
|
|
||||||
|
assert json_response(conn, 200) == %{"status" => "success"}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -18,7 +18,7 @@ test "a create activity with a note" do
|
||||||
user = insert(:user)
|
user = insert(:user)
|
||||||
other_user = insert(:user, %{nickname: "shp"})
|
other_user = insert(:user, %{nickname: "shp"})
|
||||||
|
|
||||||
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!"})
|
{:ok, activity} = CommonAPI.post(user, %{"status" => "Hey @shp!", "visibility" => "direct"})
|
||||||
|
|
||||||
result = ActivityView.render("activity.json", activity: activity)
|
result = ActivityView.render("activity.json", activity: activity)
|
||||||
|
|
||||||
|
@ -47,7 +47,8 @@ test "a create activity with a note" do
|
||||||
"tags" => [],
|
"tags" => [],
|
||||||
"text" => "Hey @shp!",
|
"text" => "Hey @shp!",
|
||||||
"uri" => activity.data["object"]["id"],
|
"uri" => activity.data["object"]["id"],
|
||||||
"user" => UserView.render("show.json", %{user: user})
|
"user" => UserView.render("show.json", %{user: user}),
|
||||||
|
"visibility" => "direct"
|
||||||
}
|
}
|
||||||
|
|
||||||
assert result == expected
|
assert result == expected
|
||||||
|
|
|
@ -49,6 +49,14 @@ test "returns the ActivityPub actor URI for an ActivityPub user" do
|
||||||
{:ok, _data} = WebFinger.finger(user)
|
{:ok, _data} = WebFinger.finger(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "returns the ActivityPub actor URI for an ActivityPub user with the ld+json mimetype" do
|
||||||
|
user = "kaniini@gerzilla.de"
|
||||||
|
|
||||||
|
{:ok, data} = WebFinger.finger(user)
|
||||||
|
|
||||||
|
assert data["ap_id"] == "https://gerzilla.de/channel/kaniini"
|
||||||
|
end
|
||||||
|
|
||||||
test "returns the correctly for json ostatus users" do
|
test "returns the correctly for json ostatus users" do
|
||||||
user = "winterdienst@gnusocial.de"
|
user = "winterdienst@gnusocial.de"
|
||||||
|
|
||||||
|
@ -80,6 +88,12 @@ test "it gets the xrd endpoint for hubzilla" do
|
||||||
|
|
||||||
assert template == "https://macgirvin.com/xrd/?uri={uri}"
|
assert template == "https://macgirvin.com/xrd/?uri={uri}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "it gets the xrd endpoint for statusnet" do
|
||||||
|
{:ok, template} = WebFinger.find_lrdd_template("status.alpicola.com")
|
||||||
|
|
||||||
|
assert template == "http://status.alpicola.com/main/xrd?uri={uri}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "ensure_keys_present" do
|
describe "ensure_keys_present" do
|
||||||
|
|
Loading…
Reference in a new issue